Trying to compile the following code:
struct Foo
{
explicit Foo ( void ) { }
explicit Foo ( Foo&& rhs ) { }
};
Foo bar ( void )
{
return Foo();
}
Getting the following error:
call to implicitly-deleted copy constructor of 'Foo'
Well, it's quite obvious that the copy-ctor is implicitly deleted.
Question 1: Why does the compiler need the copy-ctor of Foo
? I expected the return value of bar
to be constructed from the rvalue Foo()
with move-ctor.
Then I redeclare the move-ctor as implicit and everything compiles successfully.
Question 2: Why the compiler does not need copy-ctor any more when I redeclare the move-ctor as implicit?
Question 3: What does explicit
keyword mean in the context of copy and move ctors as it definitely means something different from from the context of regular ctors.
Implicitly-defined move constructor For non-union class types (class and struct), the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument.
A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. For more information about move semantics, see Rvalue Reference Declarator: &&. This topic builds upon the following C++ class, MemoryBlock , which manages a memory buffer. C++ Copy. // MemoryBlock.
Move Constructor And Semantics: std::move() is a function used to convert an lvalue reference into the rvalue reference. Used to move the resources from a source object i.e. for efficient transfer of resources from one object to another.
The move assignment operator is different than a move constructor because a move assignment operator is called on an existing object, while a move constructor is called on an object created by the operation. Thereafter, the other object's data is no longer valid.
It is because returning a value is considered an implicit conversion.
Quoting from the C++11 standard:
6.6.3 The return statement
2 [...]
A return statement with an expression of non-void type can be used only in functions returning a value; the value of the expression is returned to the caller of the function. The value of the expression is implicitly converted to the return type of the function in which it appears. A return statement can involve the construction and copy or move of a temporary object (12.2). [...]
The conversion from the return expression to the temporary object to hold the return value is implicit. So just as this would result in an error
Foo f = Foo(); // Copy initialization, which means implicit conversion
having code as your example would also trigger a similar one.
Question 1: Why does the compiler need the copy-ctor of Foo? I expected the return value of bar to be constructed from the rvalue Foo() with move-ctor.
It is because Foo(Foo&&)
is not a viable overload, because of the reasons above. The rules state that whenever the move constructor cannot be used, it is when the compiler shall then consider the copy constructor, which, in your case, is implicitly deleted due to the presence of a user-defined move constructor.
Question 2: Why the compiler does not need copy-ctor any more when I redeclare the move-ctor as implicit?
It is because your move constructor can be used now. Thus, the compiler can immediately use it without even considering the presence of the copy constructor.
Question 3: What does explicit keyword mean in the context of copy and move ctors as it definitely means something different from from the context of regular ctors.
IMHO, it doesn't make sense, and it only leads to problems, just as in your case.
The return type of bar
is Foo
. There is no copy constructor, and an explicit move constructor cannot work because an implicit conversion between Foo&&
and Foo
is still required. In this sense, explicit Foo(Foo&&)
is no different to any other explicit
converting constructor. The problem wold still occur with a conversion from a different type. This is an analogous example using int
:
struct Foo
{
explicit Foo(int) {}
};
Foo bar ( void )
{
return 42; // error: could not convert '42' from 'int' to 'Foo'
}
Q1: Because there is nothing else it can use.
Q2: Because it uses the move constructor to implicitly convert from Foo&&
to Foo
.
Q3: It means the same as it does with plain converting constructors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With