Home > Net >  Why rvalue reference member would be const?
Why rvalue reference member would be const?

Time:10-04

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)
    {}    
};
  • Related