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

时间:2018-02-23 19:52:31

标签: c++ abi rvo 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构造到位。

编辑: 如果没有N?RVO:

,这就是我想象get_string调用程序的操作方式
  1. 调用get_string()
  2. get_string结果现在在堆栈上,调用者使用它来构造s
  3. 现在使用RVO

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

1 个答案:

答案 0 :(得分:8)

调用者为返回对象分配空间,无论如何。从调用者的角度来看,函数是否使用RVO并不重要。

你也混淆了两个单独的副本。有一个RVO,它将函数局部变量的副本省略到返回值,并且从函数返回值到被初始化的对象的另一个副本也经常被删除。

基本上,没有任何省略,你可以把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缓冲区,一次从retval到{{1 }}

现在,应用NRVO后,只会更改s的定义:

get_string

从来电者的角度来看,一切都没有改变。该函数只是直接初始化它将返回到调用者为返回值分配的空间的对象。现在,字符串只移动一次:从void get_string(void* retval) { std::string& ret = *new(retval) std::string; // do stuff to ret } retval

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

s

通过这种方式,char retval[sizeof(std::string)]; get_string(retval); std::string& s(*(string*)retval); 直接由s初始化,并且不会执行任何副本或移动。