mercredi 24 avril 2019

Move construtor for returned objects breaks C++98 code?

The Standard doesn't not require a compiler to perform return-value-optimization(RVO), but then, since C++11, the result must be moved.

It looks as if, this might introduce UB to/break code, which was valid in C++98.

For example:

#include <vector>
#include <iostream>

typedef std::vector<int> Vec;
struct Manager{
    Vec& vec;
    Manager(Vec& vec_): vec(vec_){}
    ~Manager(){
        //vec[0]=42; for UB
        vec.at(0)=42;
    }
};

Vec create(){
    Vec a(1,21);
    Manager m(a);
    return a;
}

int main(){
    std::cout<<create().at(0)<<std::endl;
}

When compiled with gcc (or clang for that matter) with -O2 -fno-inline -fno-elide-constructors (I'm using std::vector with these build-option, in order to simplify the example. One could trigger the same behavior without these options with handmade-classes and a more complicated create-function) everything is OK for C++98(-std=c++98):

  1. return a; triggers copy-constructor, which leaves a intact.
  2. Destructor of m is called (must happens before a is destructed, because m is constructed after a). Accessing a in destructor is unproblematic.
  3. Destructor of a is called.

The result is as expected: 21 is printed (here live).

The situation is however different when built as C++11(-std=c++11):

  1. return a; triggers move-constructor, which "destroys" a.
  2. Destructor of m is called, but now accessing a is problematic, because a was moved and no longer intact.
  3. vec.at(0) throws now.

Here is a live-demonstration.

Am I missing something and the example is problematic in C++98 as well?

Aucun commentaire:

Enregistrer un commentaire