Why can't I return a class containing a std::unique_ptr
, using std::move
semantics (I thought), as in the example below? I thought that the return would invoke the move ctor of class A
, which would std::move
the std::unique_ptr
. (I'm using gcc 11.2, C 20)
Example:
#include <memory>
class A {
public:
explicit A(std::unique_ptr<int> m): m_(std::move(m)) {}
private:
std::unique_ptr<int> m_;
};
A makit(int num) {
auto m = std::make_unique<int>(num);
return std::move(A(m)); // error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'x86-64 gcc 11.2 #1
}
int main() {
auto a = makit(42);
return 0;
}
I believe the solution is to return a std::unique_ptr
, but before I give in I wonder why the move approach doesn't work.
CodePudding user response:
I thought that the return would invoke the move ctor of class A, which would
std::move
thestd::unique_ptr
.
All true, but the move constructor only moves the members of A
. It cannot move unrelated satellite unique pointers for you.
In the expression A(m)
, you use m
as an lvalue. That will try to copy m
in order to initialize the parameter m
of A::A
(btw, terrible naming scheme to reason about all of this). If you move that, i.e. A(std::move(m))
, the expression becomes well-formed.
And on that subject, the outer std::move
in std::move(A(...))
is redundant. A(...)
is already an rvalue of type A
. The extra std::move
does nothing good here.
CodePudding user response:
In short, you should not return
A
usingstd::move
since the compiler will do the optimization work for you (through a trick called RVO: What are copy elision and return value optimization?).Another point is that the
std::move
inm_(std::move(m))
is totally unnecessary since the coping of theunique_ptr
will happen regardless of usingstd::move
or not. Remember thatstd::move
does not guarantee the move operation and does not prevent coping in all cases.
In conclusion, you better use return A( std::move(m) );
and not return std::move(A(m));
. In your return statement, A(m)
is already a prvalue and you don't need to cast it to an xvalue using std::move
in order to return it efficiently. Just return it by value and the compiler will do the trick for you.