Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

NRVO. Turning off elision. C++11 vs C++17

I am looking at copy elision and RVO/NRVO. When I run g++ with -fno-elide-constructors I see different behaviour for c++11 and c++17.

I understand that c++17 mandates RVO under certain circumstances. I would have assumed that the below counts as NRVO (unless the compiler first eliminates the named variable y too?)

struct rvo 
{       
        rvo () : val {0} { std::cout << "Default Constructor" << std::endl;};
        ~rvo (){ std::cout << "Destructor" << std::endl;        };
        rvo(const rvo& in){ std::cout << "Copy Constructor" << std::endl; }
        rvo(rvo&& in){ std::cout << "Move Constructor" << std::endl; }
        
        double val;
};

rvo f1(rvo& x){ 
        rvo y {x};
        y.val++;
        return y;
}

int main()
{
        rvo A{};
        rvo B {f1(A)};
        return 0;
}

Output when compiled up with no additional flags is as expected

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Default Constructor
Copy Constructor
Destructor
Destructor

When compiled up with -fno-elide-constructors and --std=c++11 the output is

Default Constructor
Copy Constructor
Move Constructor
Destructor
Move Constructor
Destructor
Destructor
Destructor

And this is fine as the temporary is being created.

My question is that when I compile up with -fno-elide-constructors and --std=c++17, it eliminates one of the moves.

Default Constructor
Copy Constructor
Move Constructor
Destructor
Destructor
Destructor

It appears that it is eliminating the temporary return variable, and doing a move from the function variable y back to B in main. Even though I have switched off elision. Is it considering it as mandatory RVO or am I misunderstanding something basic?

I found this example, RVO/NRVO can not be disabled in C++17 in Clang++? but that one is obvious RVO whereas I thought my example was NRVO and thus not mandatory.

Thanks very much if anyone can confirm.

>Solution :

This is because from there is mandatory copy elision and instead the object B is created directly from the return value f(A) so that there is no use of a move constructor or a copy ctor and hence the output.

This can be seen from copy elision documentation:

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

  • In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:
T x = T(T(f())); // only one call to default constructor of T, to initialize x
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading