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 &¶m)
{
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;
}
您有以下选项。
选项 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});
在我的项目中,我需要包含指向数据单元实例的智能指针的容器。我写了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 &¶m)
{
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;
}
您有以下选项。
选项 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});