关于编译器和链接器如何处理函数调用 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调用程序的操作方式现在使用RVO
答案 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
被复制(或移动,在本例中为两次):一次从ret
到retval
缓冲区,一次从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
初始化,并且不会执行任何副本或移动。