Search code examples
c++c++11setmovesmart-pointers

How to move a unique_ptr from one set to another? (C++)


The code below is giving the error: Call to deleted constructor of 'std::unique_ptr<int>' 'unique_ptr' has been explicitly marked deleted here passing argument to parameter 'item' here.

Could someone please explain why this is? I would have thought everything would be fine because I'm using std::move in the call to foo.add.

#include <iostream>
#include <memory>
#include <set>

class Foo {
 public:
  void add(std::unique_ptr<int> item) {
    set.emplace(std::move(item));
  }
 private:
  std::set<std::unique_ptr<int>> set;
};

int main() {
  Foo foo;

  std::set<std::unique_ptr<int>> set;
  set.emplace(std::make_unique<int>(1));
  set.emplace(std::make_unique<int>(2));
  set.emplace(std::make_unique<int>(3));

  for (auto &item : set) {
    foo.add(std::move(item)); // error on this line
  }

  return 0;
}


Solution

  • Use c++ 17 extract() function.

    example

    #include <set>
    #include <memory>
    #include <iostream>
    
    int main() {
        auto s = std::set<std::unique_ptr<int>>{};
    
        s.insert(std::make_unique<int>(10));
    
        std::cout << s.size() << "\n";
    
        auto it = s.extract(s.begin());
    
        // Pointer type here just for clarification
        std::unique_ptr<int> new_ptr = std::move(it.value());
        
        std::cout << s.size() << "\n";
        
        std::cout << *new_ptr << "\n";
    }
    

    Then instead of your for each loop you might use a while loop:

    while (!set.empty()) {
       auto it = set.extract(set.begin());
       foo.add(std::move(it.value());
    }