且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

GCC NRVO / RVO警告

更新时间:2022-10-15 16:29:24

我不知道任何gcc特定的诊断信息或其他方法可以轻松解决您的任务。正如你发现的那样, -fno-elide-constructors 将会禁用复制/移动的选择,所以你会知道(N)RVO在这种情况下不会发生至少。



然而,快速浏览此C ++ 11工作草案指出:

$ b $当符合某些条件时,即使复制/移动对象的
构造函数和/或析构函数具有边,也允许实现省略
类对象的复制/移动构造效果。在
这种情况​​下,实现将
省略的复制/移动操作的源和目标视为将
引用到同一对象的两种不同方式
,并且该对象的销毁发生在后期的
时,这两个对象在没有优化的情况下将被销毁
。在以下情况下,允许使用复制/移动操作(
,称为复制删除)的这种省略:
可以合并以消除多个副本):


  • 在具有类返回类型的函数的返回语句中,表达式是非易失性自动对象的名称(其他
    作为函数或catch-clause参数)与作为函数返回类型的cv-unqualified
    相同,复制/移动操作可以通过将自动对象直接构造到
    中省略
    函数的返回值



...



    $当未绑定到引用
(12.2)的临时类对象被复制/移动到具有相同
cv-unqualified类型的类对象时,b $ b
  • 复制/移动操作可以被
    省略,直接构造临时对象到
    的目标中省略复制/移动


    ...


    当发生copy / move elision时,本地自动对象与临时(返回)对象相同,而对象又与存储对象(存储返回值的对象)相同。所以本地自动对象与存储对象相同,这意味着指针比较将等于true。一个简单的例子可以证明这一点:

      #include< iostream> 
    #include< vector>

    std :: vector< int> testNRVO(int value,size_t size,const std :: vector< int> ** localVec)
    {
    std :: vector< int> vec(大小,值);

    * localVec =& vec;

    / *在这里做些什么.. * /

    return vec;
    }

    int main()
    {
    const std :: vector< int> * localVec = nullptr;

    std :: vector< int> vec = testNRVO(0,10,& localVec);

    if(& vec == localVec)
    std :: cout<< 应用NRVO<的std :: ENDL;
    else
    std :: cout<< 未应用NRVO<<的std :: ENDL;

    启用/禁用 -fno-elide-constructors 按预期更改打印的消息。注意:从最严格的意义上说,当(N)RVO没有发生时,指针比较可能取决于未定义的行为,因为本地自动对象不存在。



    做指针比较将增加cruft,但具有独立于编译器的优势。


    Is there any warning, which allows us to know whether NRVO/RVO performed or not, in GCC?

    I found that -fno-elide-constructors turns off NRVO/RVO, but NRVO/RVO has its own conditions to occur and sometimes does not occur. There is a need to know if NRVO/RVO occurs to understand, when extra copy-construction happens.

    I am especially interested in compile-time features. It would be nice if there were some specific #pragma GCC... (which activates the diagnostic immediately following itself) or something using static assertion mechanism.

    I am not aware of any gcc specific diagnostic message or other method that easily can solve your task. As you have found out, -fno-elide-constructors will disable copy/move elisions, so you will know for sure that (N)RVO will not happen in that case at least.

    However, a quick look at paragraph 31 in section 12.8 of this C++11 working draft states that:

    When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

    • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

    ...

    • when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

    ...

    When copy/move elision happen the local auto object is the same as the temporary (return) object, which in turn is the same as the "storage" object (where the return value is stored). So the local auto object is the same as the storage object, which means a pointer comparison will equal true. A simple example to demonstrate this:

    #include <iostream>
    #include <vector>
    
    std::vector<int> testNRVO(int value, size_t size, const std::vector<int> **localVec)
    {
       std::vector<int> vec(size, value);
    
       *localVec = &vec;
    
       /* Do something here.. */
    
       return vec;
    }
    
    int main()
    {
       const std::vector<int> *localVec = nullptr;
    
       std::vector<int> vec = testNRVO(0, 10, &localVec);
    
       if (&vec == localVec)
          std::cout << "NRVO was applied" << std::endl;
       else
          std::cout << "NRVO was not applied" << std::endl;
    }
    

    Enabling/disabling -fno-elide-constructors changes the printed message as expected. Note: in the strictest sense the pointer comparison might be depending on undefined behavior when (N)RVO does not happen, since the local auto object is non-existing.

    Doing pointer comparisons will add cruft, but with the advantage of compiler-independency.