A value can be moved even if it is passed by value

In the next program, how is it possible to complete the move if the parameter was passed by value?

Both constructor signatures; MyContainer(Myclass&& myclass) and MyContainer(Myclass myclass) work equally well, but Myclass has only a move constructor, the rest are deleted.

 #include <iostream> class Myclass { public: Myclass(int value) : value{value} {std::cout << "Constructed with value " << value << "\n";} Myclass(Myclass&& other) : value{other.value} { other.value = 0; std::cout << "Move constructed with value " << value << "\n"; } Myclass(const Myclass& other) = delete; Myclass& operator=(const Myclass& other) = delete; Myclass& operator=(Myclass&& other) = delete; ~Myclass() {std::cout << "Destructed with value " << value << "\n";} private: int value; }; class MyContainer { public: //MyContainer(Myclass&& myclass) : myclass{std::move(myclass)} { } // Shouldn't this be the constructor definition? MyContainer(Myclass myclass) : myclass{std::move(myclass)} { } // this also works, why? private: Myclass myclass; }; int main() { Myclass myclass{5}; MyContainer mycontainer{std::move(myclass)}; return 0; } 

When the constructor is called, I use std::move , but how does the compiler “remember” that it was moved if the parameter was Myclass and not Myclass&& ?

+5
source share
3 answers

When you write MyContainer mycontainer{std::move(myclass)}; , we must list all viable MyContainer constructors that can be called with an x ​​value of type Myclass . MyContainer has a single matching constructor that takes an argument of type Myclass . To ensure that this constructor is viable, we effectively perform overload resolution again when building Myclass with an x ​​value of type Myclass . This gives us three constructors:

 Myclass(int ); // not viable: no conversion from `Myclass` to `int` Myclass(Myclass&& ); // viable Myclass(const Myclass& ) = delete; // viable 

The move constructor is viable since you can bind an rvalue reference to an x ​​value. The copy constructor is viable since you can bind a const lvalue reference to something. One of the tiebreaks in overload resolution selects the smallest qualification cv. The move constructor is an rvalue reference to Myclass . The copy constructor is an lvalue reference to Myclass const - this is more of a cv qualification, so it is less preferred. Thus, we select the move constructor. This constructor is not deleted, so the call is correctly formed.

The important part here is that we are building with an xvalue of type Myclass (what std::move() does is casting to xvalue). If we were building with lvalue (i.e., just MyContainer mycontainer(myclass) ), then only the copy constructor would be viable, but it was explicitly deleted, so that the whole expression is poorly formed.

how does the compiler “remember” that it was moved if the parameter was Myclass and not Myclass&& ?

There are no memories. In both cases, we try to initialize the argument in the constructor from the x value of type Myclass . In the first case, this involves calling the Myclass move Myclass . In the latter case, this is a direct link.

+3
source

In both cases, you make your lvalue myclass reference to rvalue.

The return type is std::move is a reference to rvalue, therefore by definition the expression std::move is rvalue - and thus can bind to the standard parameter rvalue designer displacement myclass .

The std::move can be interpreted as "I am done with this variable, now it can be moved." Note that this is true no matter what type of variable is passed to std::move - it works just like normal variables, lvalue references and rvalue references.

+4
source

how is it possible to complete the move if the parameter was passed in a value?

Regular values ​​(arguments) can be copied by moving.

So, with the constructor value, the argument is initialized by copying, passing from the passed value, and then the element is initialized by copying, passing from the argument.

Using the rvalue reference constructor, the reference to the argument is bound to the passed value, and the element is initialized by the copy, moving from the reference value.

how does the compiler “remember” that it was moved if the parameter was Myclass and not Myclass&& ?

The compiler does not need to “remember” whether the object was moved or not (or it was created by moving or otherwise).

+2
source

All Articles