【问题标题】:How does C++ ABI deal with RVO and NRVO?C++ ABI 如何处理 RVO 和 NRVO?
【发布时间】:2018-08-03 22:48:09
【问题描述】:

我很困惑编译器和链接器如何处理函数调用er的要求因函数使用 RVO 还是 NRVO 而异。

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

std::string s = get_string();

如果 get_string 不执行 N?RVO 但如果 get_string 执行 N?RVO 调用代码不执行任何操作并且 s 由函数 get_string 就地构造,则涉及从 get_string 的结果移动构造。

编辑: 如果没有 N?RVO,我想象 get_string 调用者的操作方式如下:

  1. 调用 get_string()
  2. get_string 结果现在在堆栈上,调用者使用它来构造 s

现在有了 RVO

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

【问题讨论】:

  • 不是真的,我知道 RVO 从概念上讲是做什么的。我想知道它是如何在 ABI 中实现的,因为调用者需要知道被调用者是如何实现的(或者我错了吗?)。
  • 我不确定它与任何其他呼叫有何不同。它是堆栈,因此链接器不在乎。编译器只是将变量一个接一个地放在堆栈上。然后,所有变量访问都是来自当前堆栈顶部的反向引用。 在调用之前创建的变量会稍微往下一点。
  • @Galik - 编辑问题

标签: c++ abi rvo nrvo


【解决方案1】:

调用者无论如何都会为返回对象分配空间。从调用者的角度来看,函数是否使用 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 缓冲区,一次从retvals

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

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

从调用者的角度来看,没有任何改变。该函数只是直接将要返回的对象初始化到调用者为返回值分配的空间中。现在字符串只移动一次:从retvals

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

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

这样s直接由get_string初始化,不进行复制或移动。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多