无论如何,调用者都会为返回对象分配空间。从调用者的角度来看,函数是否使用 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
被复制(或移动,在这种情况下)两次:一次 fromret
到retval
缓冲区,一次从retval
to s
。
现在,应用 NRVO 后,只有 的定义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
直接由 初始化get_string
,不进行复制或移动。