std :: move()或它在局部变量上的显式等价物是否允许省略?

时间:2017-08-07 18:47:42

标签: c++ c++11 compiler-optimization move-semantics

例如:

Big create()
{
    Big x;
    return std::move(x);
//  return static_cast<typename std::remove_reference<T>::type&&>(t) // why not elide here?
}

假设应用std::move()来返回局部变量会抑制移动语义,因为编译器通常不能对函数的内部工作做出任何假设,那么在不需要这些假设的情况下,例如:

  1. std::move(x)内联(可能总是)
  2. std::move(x)写成:static_cast<typename std::remove_reference<T>::type&&>(t)
  3. 根据现行标准,允许实施NRVO ......

      

    - 在具有类返回类型的函数的return语句中,时   expression是非易失性自动对象的名称(除了   函数参数或由...引入的变量   具有相同类型的处理程序(18.3)的异常声明   (忽略cv-qualification)作为函数返回类型,复制/移动   通过直接构造自动对象可以省略操作   进入函数调用的返回对象

    显然,1)和2)都没有资格。除了使用std::move()返回局部变量的事实是多余的,为什么需要这个限制

2 个答案:

答案 0 :(得分:2)

重读这个问题后,我的理解不同了。我将此问题视为'为什么std::move()禁止(N)RVO'

引用问题中提供的标准有错误突出显示。它应该是

  

在一个带有类返回类型的函数的return语句中   expression是非易失性自动对象的名称(除了   函数参数或由...引入的变量   处理程序的异常声明(18.3))具有相同的类型   (忽略cv-qualification)作为函数返回类型

这里禁止NRVO的原因不是调用std::move(),而是std::move的返回值不是X,而是X&&与功能签名不匹配!

答案 1 :(得分:2)

你应该清楚知道什么&#34;允许elision&#34;手段。首先,编译器可以在&#34; as-if&#34;下进行任何想做的事情。规则。也就是说,只要该程序集行为正确,编译器就可以吐出它想要的任何程序集。这意味着编译器可以忽略它想要的任何构造函数,但无论是否调用构造函数,它都必须证明程序的行为是相同的。

那么为什么elision的特殊规则呢?好吧,这些情况下编译器可以忽略构造函数调用(因此,析构函数调用),而证明行为是相同的。这是非常有用的,因为有很多类型的构造函数非常重要(例如,string),并且实际中的编译器通常无法证明它们是安全的(在一个合理的时间框架)(在过去,甚至还不清楚优化堆分配是否合法开始,因为它基本上是全局变量的变异)。

因此,出于性能原因,我们希望省略。但是,它基本上在行为方面指定了标准中的特殊情况。特殊情况越大,我们引入标准的复杂性就越大。所以我们的目标应该是让elision的允许情况足够广泛,以涵盖我们关心的有用案例,但不能更广泛。

你正在接近这个:为什么不把这个特殊案例变得和实际一样大?实际上,情况正好相反。为了扩展elision的允许情况,需要证明它是非常值得的。