简体   繁体   中英

std::unique_ptr and templates: Why won't this code compile?

I am working on a templated component system for a game. It holds items in a vector and has two partial specializations: One for POD types and another that uses std::unique_ptr. The templated specialization for the vector of std::unique_ptr doesn't compile but using a vector of std::unique_ptr's in non-templated code works fine. I have searched for a solution to this problem, but unfortunately my knownledge of C++11/C++14 isn't refined enough; what am I doing wrong?

Minimal example (stripped to the problem area):

#include <vector>
#include <memory>

template<typename T>
class component 
{
    public:
    class handle
    {
    friend class component;
    protected:
        std::size_t inner;
    };

    component<T>::handle add(const T& t)
    {
        items.push_back(t);
        handle h;
        h.inner = items.size() - 1;
        return h;
    }

    protected:
        std::vector<T> items;
};


template<typename T>
class component<std::unique_ptr<T>> 
{
    public:
    class handle
    {
    friend class component;
    protected:
         std::size_t inner;
    };

    component<std::unique_ptr<T>>::handle add(const std::unique_ptr<T>& t)
    {
        items.push_back(std::move(t));
        handle h;
        h.inner = items.size() - 1;
        return h;
    }

    protected:
    std::vector<std::unique_ptr<T>> items;
};

Here is the testing code

int main()
{
    // This works fine.
    component<int> pod_component;
    pod_component.add(5);

    // This works, too!
    std::vector<std::unique_ptr<int>> pointer_vector;
    pointer_vector.push_back(std::make_unique<int>(5));

    component<std::unique_ptr<int>> pointer_component;
    // Why doesn't this compile?
    pointer_component.add(std::make_unique<int>(5));

    return 0;
 }

Here is the error output from gcc (4.9.3 Gentoo):

 error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }

I hope that someone better at C++ than myself can help both me and future readers understand this tricky question.

The problem is here:

component<std::unique_ptr<T>>::handle add(const std::unique_ptr<T>& t)   
{                                 //        |
    items.push_back(std::move(t));// <------+
    // ...

std::move applied to a const lvalue returns a const rvalue reference. Thus, it is bound by the push_back(const value_type&) overload rather than the push_back(value_type&&) one, which means that it tries to copy the unique_ptr passed as an argument.

The correct declaration should look as follows:

component<std::unique_ptr<T>>::handle add(std::unique_ptr<T>&& t)
//                                                         ~^^~

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