I am trying to write a move constructor for a structure but I can't understand why I fail to call the move constructor of structure member:
#include <memory>
struct C
{
std::unique_ptr<int[]> mVector;
size_t mSize;
C() = default;
C(C &&temp)
: mVector(temp.mVector)
, mSize(temp.mSize)
{}
};
When I compile this I get:
gcc -c TempTest.cpp
TempTest.cpp: In constructor 'C::C(C&&)':
TempTest.cpp:9:23: 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 []>]'
9 | , mSize(temp.mSize)
| ^
In file included from c:/msys64/mingw64/include/c /10.3.0/memory:83,
from TempTest.cpp:1:
c:/msys64/mingw64/include/c /10.3.0/bits/unique_ptr.h:723:7: note: declared here
723 | unique_ptr(const unique_ptr&) = delete;
| ^~~~~~~~~~
Because in contructor temp
is a rvalue reference, it is non-const so temp.mVector
should be non-const and should call the unique_ptr
move constructor but instead it calls the copy constructor which is deleted. Any idea where is the error?
CodePudding user response:
Why rvalue reference member would be const?
Don't assume that it's const
. You should assume that unique_ptr(const unique_ptr&)
is merely the best match, from the available constructors.
Because in constructor
temp
is a rvalue reference
Surprise! It is not an r-value reference.
The variable temp
is bound to an r-value, when the constructor is called. And now that it's a named variable, it's no longer a "temporary". It has become an l-value.
Since you know that the value was an r-value when the constructor was called, you can safely move the members, converting them back to an r-value.
C(C &&temp)
: mVector(std::move(temp.mVector))
// ^^^^^^^^^ We know that temp CAME FROM an r-value,
// so it can safely be moved.
, mSize(temp.mSize)
{}
CodePudding user response:
try to run the following code and everything become clear:
struct Test
{
Test(){}
Test(const Test& r)
{
std::cout << "i am using the copy constructor :) " << std::endl;
}
Test(Test&& r)
{
std::cout << "I require std::move to be selected as possible overload.." << std::endl;
}
};
int main()
{
Test first;
Test second(first);
Test third(std::move(second));
return 0;
}
std::move helps to select the right constructor overload by passing a r-value reference (Test&&) to the constructor. In your case, the compiler is selecting the copy constructor even if your vector isn't const, just because it is considered the best match (an implicit cast to a const reference is preferred to an implicit cast to a r-value reference, since the second one, differently from the first, may modify the value) adding a std::move you can select the right constructor and solve your issue.
#include <memory>
struct C
{
std::unique_ptr<int[]> mVector;
size_t mSize;
C() = default;
C(C &&temp)
: mVector(std::move(temp.mVector))
, mSize(temp.mSize)
{}
};