Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move constructor involving const unique_ptr

In the code below, I made p const because it will never point to any other int during Foo's lifetime. This doesn't compile, as the unique_ptr's copy constructor is called, which is obviously deleted. Are there any solutions besides making p non-const? Thanks.

#include <memory>

using namespace std;

class Foo 
{
public:
  //x is a large struct in reality
  Foo(const int* const x) : p(x) {};
  Foo(Foo&& foo) : p(std::move(foo.p)) {};
private:
  const unique_ptr<int> p;
};
like image 801
Agrim Pathak Avatar asked Mar 22 '15 12:03

Agrim Pathak


People also ask

Can unique_ptr be moved?

A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.

Is unique_ptr null after move?

Yes, you can compare it to nullptr after the move and it is guaranteed to compare equal. This is clearly true after calling release(). But std::move doesn't call release(). So how does the compiler know to restore the invariant of the unique_ptr?

What is the move constructor in C++?

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.

Does unique_ptr have copy constructor?

For the copy constructor of Foo , there is no similar mechanism as there is no copy constructor of unique_ptr . So, one has to construct a new unique_ptr , fill it with a copy of the original pointee, and use it as member of the copied class.


1 Answers

The semantics of your move constructor are contradictory.

You have declared a const std::unique_ptr which will (uniquely) own the value it is initialised with. But you've declared a move constructor that should move that value into another object at construction.

So what do you think should happen to the std::unique_ptr in the 'temporary' being move constructed from?

If you want it to be release()ed you've violated its constness. If you want it to retain its value you've violated the constraint of std::unique with requires no more than one such object to own any given object. Checkmate.

This problem reveals a subtle limitation of the C++ language. It requires move semantics to leave the copied to and from as valid objects.

There are several quite reasonable proposals for 'destructive move' which would in truth better reflect what most uses of move are doing - take a value to here from there 'invalidating' what was there.

Google them. I haven't made a literature survey so don't want to recommend one.

Your alternatives here are to remove const or cast it way. I strongly recommend removing it. You can make sure the semantics of your class ensure the appropriate const-ness with no impact and no 'ugly suspect' const_cast.

#include <iostream>
#include <memory>

class Foo 
{
public:
  Foo(const int x) : p(new int(x)) {};
  Foo(Foo&& foo) :
    p(std::move(foo.p)) {

    };

    int get(void)const{
        return *(this->p);
    }

private:
     std::unique_ptr<int> p;
};

Foo getMove(){
    return Foo(88);
}

int main(){

    Foo bar(getMove());    
    std::cout<<bar.get()<<std::endl;

    return EXIT_SUCCESS;
}
like image 151
Persixty Avatar answered Nov 10 '22 09:11

Persixty