C++:如何将 unique_ptr 推送到双端队列?

C++: How to push unique_ptr to deque?

在我的项目中,我需要包含指向数据单元实例的智能指针的容器。我写了class(简单的例子):

template <typename T>
class Queue
{
public:

    void push(const T & param) 
    {
        m_deque.push_front(param);
    }

private:
    std::deque<T> m_deque;
};

比我要推的那个:

int main()
{

    Queue< std::unique_ptr<DataBox> > queue;

    std::unique_ptr<DataBox> d1(new DataBox(11));

    queue.push(d1);

    return 0;
}

而 VS2017 编译器说我不能这样做:

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

据我了解,错误的原因是试图复制 unique_ptr。但是如果我将签名更改为:

void push(T && param) {...}

和函数调用

queue.push( std::move(d1) );

我又遇到这个错误了。所以问题是 - 我应该如何实现 push()unique_ptr 移到队列?

您应该修改 class 以处理仅移动类型:

template <typename T>
class Queue
{
public:

    void push(const T & param) 
    {
        m_deque.push_front(param);
    }

    void push(T&& param) 
    {
        m_deque.push_front(std::move(param));
    }


private:
    std::deque<T> m_deque;
};

使用情况:

int main()
{
    Queue< std::unique_ptr<DataBox> > queue;

    std::unique_ptr<DataBox> d1(new DataBox(11));

    queue.push(std::move(d1));
}

如果你想让它通用,这样 push 可以取 l 或 r 值,你可以尝试这样的事情:

#include <iostream>
#include <memory>
#include <deque>
#include <utility>

class DataBox 
{
public:
    DataBox(int i) {}
};

template <typename T>
class Queue
{
public:
    template <typename U>
    void push(U &&param)
    {
        m_deque.push_front(std::forward<T>(param));
    }

private:
    std::deque<T> m_deque;
};      

int main()
{
    Queue<std::unique_ptr<DataBox>> queue;
    std::unique_ptr<DataBox> d1 = std::make_unique<DataBox>(11);
    std::unique_ptr<DataBox> d2 = std::make_unique<DataBox>(22);
    queue.push(d1);
    queue.push(std::move(d2));

    return 0;
}

https://ideone.com/ixzEmN

您有以下选项。

选项 1:简洁且通常速度快

template <typename T>
class Queue {
public:
    void push(T param) {
        m_deque.push_front(std::move(param));
    }

private:
    std::deque<T> m_deque;
};

我会在大多数实际情况下推荐这个,因为它很简单。但是,如果 T 不能有效移动,它将复制两份而不是一份。

选项 2:总是快

template <typename T>
class Queue {
public:
    void push(const T& param) {
        m_deque.push_front(param);
    }
    void push(T&& param) {
        m_deque.push_front(std::move(param));
    }

private:
    std::deque<T> m_deque;
};

这是最佳解决方案,但可能有点矫枉过正。如果 T 是有效移动的(并且你应该努力使所有类型都有效地移动),那么它所做的副本数量与选项 1 完全相同:

Queue<std::vector<int>> q;
std::vector<int> v;
q.push(v);             // one copy, both with option 1 and option 2
q.push(std::move(v));  // no copies, both with option 1 and option 2

但是:

Queue<NotEfficientlyMovableType> q;
NotEfficientlyMovableType x;
q.push(x);             // one copy with option 2, two copies with option 1
q.push(std::move(x));  // same (so it doesn't really make sense)

选项 3:简洁,始终快速,但有注意事项

template <typename T>
class Queue {
public:
    template <typename U>
    void push(U&& param)
    {
        m_deque.push_front(std::forward<T>(param));
    }

private:
    std::deque<T> m_deque;
};

使用选项 3,您只能编写一个函数,并且它始终执行最少的副本(如选项 2)。但是,它需要模板参数推导,这会破坏有效代码。例如,这适用于选项 1 和 2,但不适用于选项 3:

Queue<std::pair<int, int>> q;
q.push({1, 2});