C++ ABI如何处理RVO和NRVO?

How does C++ ABI deal with RVO and NRVO?

我对编译器和链接器如何处理函数调用er 的要求因函数使用 RVO 或 NRVO 而不同这一事实感到困惑。

这可能是我的误解,但我的假设是一般没有RVO或NRVO

std::string s = get_string();

如果 get_string 不执行 N?RVO,则涉及从 get_string 的结果移动 s 的构造,但如果 get_string 执行 N?RVO 调用代码什么都不做,并且 s 由函数 get_string.

就地构建

编辑: 这是我想象 get_string 如果没有 N?RVO:

调用者的操作方式
  1. 呼叫get_string()
  2. get_string 结果现在在堆栈上,调用者使用它来构造 s

现在有了 RVO

  1. 呼叫get_string()
  2. 当get_string完成时栈上没有结果,get_string构造s,调用者不需要做任何构造s。

无论如何调用者都会为return对象分配space。从调用者的角度来看,函数是否使用RVO并不重要。

您还混淆了两个单独的复制省略。有 RVO,它省略了从函数局部变量到 return 值的副本,还有另一个从函数 return 值到被初始化对象的副本,它也经常被省略。

基本上,在没有任何省略的情况下,您可以认为来自 OP 的调用看起来像这样(忽略任何别名问题,这实际上都将直接在汇编中实现):

void get_string(void* retval)
{
    std::string ret;
    // do stuff to ret
    new(retval) std::string(std::move(ret));
}

char retval[sizeof(std::string)];
get_string(retval);
std::string s(std::move(*(string*)retval));

字符串 ret 被复制(或移动,在本例中)两次:一次从 retretval 缓冲区,一次从 retvals.

现在,应用 NRVO 后,只有 get_string 的定义会发生变化:

void get_string(void* retval)
{
    std::string& ret = *new(retval) std::string;
    // do stuff to ret
}

从来电者的角度来看,没有任何变化。该函数只是直接将它要 return 的对象初始化为调用者为 return 值分配的 space 。现在字符串只移动了一次:从 retvals.

现在调用者也可以省略一个副本,因为不需要分配一个单独的 return 值,然后将其复制到正在初始化的对象中:

char retval[sizeof(std::string)];
get_string(retval);
std::string& s(*(string*)retval);

这样,s直接被get_string初始化了,没有进行复制和移动