Move constructor involving const unique_ptr

Agrim Pathak picture Agrim Pathak · Mar 22, 2015 · Viewed 10.7k times · Source

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;
};

Answer

Persixty picture Persixty · Mar 22, 2015

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;
}