了解关于c ++ 11的遵守规则

时间:2012-02-13 20:41:02

标签: c++ c++11

我一直在测试rvalue引用并移动语义,并希望确保我理解何时应该省略副本以及何时应该遵循移动语义。

鉴于以下内容

class NRVCA
{
public:
    NRVCA(int x):
    {}
    NRVCA(const NRVCA & Rhs)
    {}    
    NRVCA& operator=(const NRVCA& dref)
    {}          
};

NVCRA GetATemp()
{       
   return NVCRA(5);
} 

NVCRA GetACopy()
{
   NVCRA ret(5);
   ...
   return ret;  
}

int main()
{ 
    //This call will be elided allays and invoke the single param constructor 
    NVCRA A = GetATemp();
    //This call will be a traditional copy the complier may elide this 
    // if so the work will be done inline 
    NVCRA B = GetACopy();

}

在这种情况下,移动语义不起作用,与c ++ 11中C ++ 03的唯一区别在于,而不是编译器被允许忽略它们需要忽略。

所以 问题1.在什么情况下,我保证复制构造函数将会或不会被删除。

问题2.是否有办法强制编译器不要忽视。

问题3.假设您具有逻辑上一致的复制操作,是否有任何逻辑上的原因我不希望编译器这样做。

问题4.如果我定义移动构造函数,则无论如何都不会删除副本。这应该影响我的课程设计。

3 个答案:

答案 0 :(得分:7)

  1. 删除副本和移动始终是可选的优化,标准不保证何时完成。 Elision与选择移动复制的编译器不同。根据正常的重载决策规则选择移动构造或移动分配时,语言保证。

  2. 有些编译器会提供一个标志来关闭elision。 gcc和clang有-fno-elide-constructors。 MSVC没有特定选项,但禁用优化可能会避免某些缺陷(但有些不能被关闭,例如Foo x = 1;中的副本)

  3. 我不知道有什么理由不在生产版本中删除副本/动作。

  4. 有些人建议在返回'重'级时不要依赖'返回值优化',因为RVO不能保证。就个人而言,我刚刚验证了我的编译器对它的了解并继续进行。现在可以移动对象了,你不再需要担心编译器是否支持这样的优化,因为即使它没有,你仍然会得到移动而不是副本。

答案 1 :(得分:4)

引用C ++ 11标准(12.8 / 31),“当满足某些条件时,允许实现 省略类对象的复制/移动构造,即使副本对象的/ move构造函数和/或析构函数有副作用。“,所以:

  1. 不保证复制省略。 (技术上;但出于营销原因,编译器供应商的动机是这样的)
  2. 这是一个特定于编译器的问题。
  3. 从技术上讲,有些情况(see here)但是它们会在设计中指出(IMO)一些错误。
  4. 您仍然应该提供移动构造函数(如果可以有效地实现它)。在某些情况下,禁止复制省略,但移动构造函数就可以了:

    vector<string> reverse(vector<string> vec)
    {
        reverse( vec.begin(), vec.end() );
        return vec;
    }
    
    auto vec = reverse(vector<string>{"abc", "def", "ghi"});
    
  5. 显式不允许复制省略从函数参数传播到从临时初始化的最终自动值。但是由于移动构造函数没有调用副本。


    为了更具体地说明我的上一句话,我引用N3290,这是现在很难得到的,但它非常接近N3337

    12.8 / 31:

      

    当满足某些条件时,允许实现省略类的复制/移动构造   对象,即使对象的复制/移动构造函数和/或析构函数具有副作用。在这种情况下,   该实现将省略的复制/移动操作的源和目标视为两个不同的   引用相同对象的方式,以及对象的破坏发生在时间的晚期   当两个对象在没有优化的情况下被销毁的时候。这种复制/移动的省略   在下列情况下允许称为复制省略的操作(可以合并为   消除多份副本):

         

    - 在具有类返回类型的函数的return语句中,当表达式是a的名称时   具有相同cv-nonqualified的非易失性自动对象(除函数或catch子句参数之外)   键入函数返回类型,可以通过构造省略复制/移动操作   自动对象直接进入函数的返回值

    12.8 / 32:

      

    当满足复制操作的省略标准或将满足时,除了源对象是函数参数这一事实,并且要复制的对象由左值指定,首先执行重载决策以选择副本的构造函数,就好像该对象是由rvalue指定的一样。如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值。 [...]

答案 2 :(得分:2)

  1. 我不相信即使复制构造函数有副作用,编译器何时可以选择删除副本也有任何保证。标准明确允许这样做。

  2. 哪个编译器?例如,我相信你可以关闭优化而不是在g ++中获得省略。语言中的无法强制任何编译器生成一个认为可以省略的复制构造函数。即使复制构造函数有副作用,该标准也明确允许省略。

  3. 我无法想到不要忽视的理由。

  4. 作为一般规则,复制省略不应影响班级的逻辑设计。首先设计您的课程,然后如果您遇到性能问题,请使用分析器来帮助改进它。