简体   繁体   中英

Wrapping a unique_ptr with custom class in a container

I took my first C++ class in 1990, long before your newfangled exceptions, STL and whatnot. Now I am writing a custom C++ container and I decided I would use this as an opportunity to learn some C++11 techniques and concepts, especially unique_ptr. Unfortunately I am having some trouble with the move semantics (I think) when inserting an element. Here is a very stripped down version of the code I am trying to get to compile:

#include <vector>
#include <memory>

struct Key {
    int k_;
    Key() : k_(0){};
    explicit Key(int k) : k_(k){};
    Key(const Key &o) : k_(o.k_) {}
    Key(Key &&o) { k_ = std::move(o.k_); }

    Key &operator=(const Key &o) {
        k_ = o.k_;
        return *this;
    }
    Key &operator=(Key &&o) {
        k_ = std::move(o.k_);
        return *this;
    }
    int get() const { return k_; }
};

template <class T> class CustomContainer {
public:
    typedef std::pair<Key, std::unique_ptr<Key>> Record;

    CustomContainer() {}
    ~CustomContainer(){};

    bool insert(const Record &record) {
        objects.emplace_back(std::move(record));
        return true;
    }
    std::vector<Record> objects;
};

int main() {
    CustomContainer<Key> q;
    q.insert(CustomContainer<Key>::Record(Key(1), std::unique_ptr<Key>(new Key(1))));
}

I am inserting a pointer to a Key object to keep the code simple. In my actual application, Key is a little more complicated, T is not Key, and the Custom container has many more member functions, but this is enough to highlight the problem. When I just have a unique_ptr object in the vector, everything appears to work just fine. As soon as I add the pair, I get:

/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/ext/new_allocator.h:120:23: error: call to
      implicitly-deleted copy constructor of 'std::pair<Key, std::unique_ptr<Key, std::default_delete<Key> > >'
        { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
                             ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
.
.
.

simple.cc:33:13: note: in instantiation of function template specialization 'std::vector<std::pair<Key,
      std::unique_ptr<Key, std::default_delete<Key> > >, std::allocator<std::pair<Key, std::unique_ptr<Key,
      std::default_delete<Key> > > > >::emplace_back<const std::pair<Key, std::unique_ptr<Key,
      std::default_delete<Key> > > >' requested here
    objects.emplace_back(std::move(record));
            ^
simple.cc:41:5: note: in instantiation of member function 'CustomContainer<Key>::insert' requested here
  q.insert(CustomContainer<Key>::Record(Key(1), std::unique_ptr<Key>(new Key(1))));

I tried the same thing with a custom class instead of a pair and got the same error. I can't seem to get the compiler to call the move constructor instead of the copy constructor no matter how many std::move()s I add. What am I missing?

you're passing a const ref to a unique_ptr and then trying to copy from it (you can only move from a non-const). Pass the entire object and then move from that. Since you're initialising with a temporary (r-value reference), there will be an implicit move at the call site.

patch to fix your code is here:

template <class T> class CustomContainer {
public:
   ...
    bool insert(Record record) { // <-- FIXED
       ...
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM