Home > Back-end >  Why is the move constructor not invoked when returning an rvalue?
Why is the move constructor not invoked when returning an rvalue?

Time:04-18

I have created a class Animal:

class Animal {
public:
    Animal() = default;
    Animal(Animal&& a) = delete;
    Animal(Animal& a) = delete;

    Animal& operator=(Animal&& a) = delete;
    Animal& operator=(Animal& a) = delete;
};

Animal func1() {
    return Animal();
}

Animal func2() {
    Animal a {};
    return a;
}

int main(void) {
    Animal a1 = func1();
    Animal a2 = func2();
    return 0;
}

I don't understand why func1() works fine: return Animal() creates an rvalue object and I initialize a1 with this object in the main function; as I see it, it is equal to

Animal a1 = func1()
         ==
Animal a1 = Animal&& temp   //(I wrote type in assignment for clarifying)

and I have read that the return value is an rvalue; however, in func2 I get an error that ‘copy constructor is deleted’ not the move constructor, why?

CodePudding user response:

Case 1

Here we consider the statement:

 Animal a1 = func1();

The call expression func1() is an rvlalue of type Animal. And from C 17 onwards, due to mandatory copy elison:

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 a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:

That is, the object is constructed directly into the storage where they would otherwise be copied/moved to. That is, in this case(for C 17), there is no need of a copy/move constructor to be available. And so this statement works.

Case 2

Here we consider the statement:

Animal a2 = func2();

Here from non mandatory copy elison,

Under the following circumstances, the compilers are permitted, but not required to omit the copy and move (since C 11) construction of class objects even if the copy/move (since C 11) constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. This is an optimization: even when it takes place and the copy/move (since C 11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed:

  • In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type.

That is, the copy/move constructors are required to exist(that is these ctors must be present and accessible) but since you've explicitly marked them as deleted this statement fails with the error:

error: use of deleted function ‘Animal::Animal(Animal&&)’

The error can also be seen here

CodePudding user response:

In C 17 there is a new set of rules about temporary materialization.

In a simple explanation an expression that evaluates to a prvalue (a temporary) doesn't immediately create an object, but is instead a recipe for creating an object. So in your example Animal() doesn't create an object straight away so that's why you can return it even if the copy and move constructors are deleted.

In your main assigning the prvalue to a triggers the temporary materialization so the object is only now created directly in the scope of main. Throught all of this there is a single object so there is no operation of copy or move.

  • Related