std::unique_ptr 和模板:为什么这段代码无法编译?

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

本文关键字:代码 编译 段代码 ptr unique 为什么 std      更新时间:2023-10-16

我正在制作一款游戏的模板化组件系统。它将元素保存在vector中,并有两个部分专门化:一个用于POD类型,另一个使用std::unique_ptr。std::unique_ptr的vector的模板化特化不能编译,但在非模板化代码中使用std::unique_ptr的vector可以正常工作。我一直在寻找这个问题的解决方案,但不幸的是,我对c++ 11/c++ 14的了解不够完善;我做错了什么?

最小示例(剥离到问题区域):

#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;
};
下面是测试代码
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;
 }
下面是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)...); }

我希望有比我更精通c++的人可以帮助我和未来的读者理解这个棘手的问题。

问题就在这里:

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

std::move应用于const左值返回const右值引用。因此,它受push_back(const value_type&)重载而不是push_back(value_type&&)重载的约束,这意味着它试图复制作为参数传递的unique_ptr

正确的声明如下:

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