3
votes

I have an std::unordered_set of pointers. If I do

int main()
{
  std::unordered_set<std::unique_ptr<int>> set;
  set.insert(std::make_unique(3));
}

everything works and insert calls the move constructor (i think). However I have a different situation that is similar to the following code

std::unordered_set<std::unique_ptr<int>> set;
void add(const std::unique_ptr<int>& obj) {set.insert(obj);}

int main
{
  std::unique_ptr<int> val = std::make_unique<int>(3);
  add(std::move(val));
}

and this does not compile. It gives

State Error C2280 'std::unique_ptr<int,std::default_delete>::unique_ptr(const std::unique_ptr<int,std::default_delete> &)': attempting to reference a deleted function

which means that it's trying to call the copy constructor (right?). So the question is: why insert doesn't move the object when it is inside a function? I tried also passing the pointer to the unique_ptr and even adding std::move pretty much everywhere but the copy constructor is always called

2

2 Answers

6
votes
void add(const std::unique_ptr<int>& obj) 

It comes down to something fundamental: you cannot move a constant object, by definition. "Move" basically means ripping the guts out of the moved-from object, and shoving all the guts into the new, moved-to object (in the most efficient manner possible, with the precision of a surgeon). And you can't do it if the moved-from object is const. It's untouchable. It must remain in its original, pristine condition.

You should be able to do that if the parameter is an rvalue reference:

void add(std::unique_ptr<int>&& obj) {set.insert(std::move(obj));}
0
votes

std::unique_ptr by definition does not have a copy constructor. It does have a move constructor. But your unique pointer here is a const, so the move constructor cannot be called