我的代码:
template <typename T>//, T value_if_empty>
class AtomicQueue {
std::mutex m;
std::queue<T> q;
T value_if_empty;
public:
AtomicQueue(const T& emptyval) : value_if_empty(std::move(emptyval)) {};
void push(const T& t)
{
m.lock();
q.push(std::move(t));
m.unlock();
}
T pop()
{
T a = std::move(value_if_empty);
m.lock();
if (!q.empty())
{
a = std::move(q.front());
q.pop();
}
m.unlock();
return a;
}
};
AtomicQueue<int> A(0);
AtomicQueue<std::unique_ptr<Class1>> B(nullptr);
AtomicQueue < std::pair<int, std::unique_ptr<Class1>>> C({ 0,nullptr });
如果仅声明A和B,则此代码将编译,但同时声明C时,此代码将失败。错误是
'std::pair<int,std::unique_ptr<Class1,std::default_delete<Class1>>>::pair(const std::pair<int,std::unique_ptr<Class1,std::default_delete<Class1>>> &)': attempting to reference a deleted function
问题似乎是
std::pair<int, std::unique_ptr<Class1>>
没有副本构造函数。但是std::unique_ptr<Class1>
也是如此。那么为什么B的声明可以编译而C则不能呢?另外,在哪里使用复制构造函数?抱歉,答案很简单。我是使用unique_ptr和移动构造函数的新手。
附带问题:是否有使用value_if_empty的更干净或更佳的方法?我不希望在使用unique_ptr时传递nullptr以外的任何东西。
编辑:B也不编译。 (如NathanOliver的评论中所述)。因此,现在的问题是如何消除此错误?
最佳答案
这是因为您不会在函数中移动任何东西。他们收到const T&
,但是常量不能移动。在std::move
上调用const
将不会移动,并且绝对不会执行任何操作。
看,移动只是对右值的强制转换。实际移动发生在移动构造函数和移动分配中。这样的move构造函数的声明如下:
unique_ptr(unique_ptr&& other);
如您所见,它是一个非常量右值引用。由于无法调用move构造函数,因此它将尝试复制。这就是错误的根源。
那你怎么解决呢?
只需添加所需的重载并删除多余的 Action :
template <typename T>
class AtomicQueue {
std::mutex m;
std::queue<T> q;
T value_if_empty;
public:
// copy, it's an lvalue
AtomicQueue(const T& emptyval) : value_if_empty(emptyval) {};
// move, it's an rvalue
AtomicQueue(T&& emptyval) : value_if_empty(std::move(emptyval)) {};
void push(const T& t)
{
m.lock();
q.push(t); // same here
m.unlock();
}
void push(T&& t)
{
m.lock();
q.push(std::move(t)); // same here
m.unlock();
}
T pop()
{
T a = std::move(value_if_empty);
m.lock();
if (!q.empty())
{
a = std::move(q.front());
q.pop();
}
m.unlock();
return a;
}
};
Live example
另请注意,您的类(class)中存在一个基本错误。看这行:
T a = std::move(value_if_empty);
如果
pop
被多次调用,则value_if_empty
将从值中移出并返回。在返回未指定的值之前,您的pop
函数只能被调用一次。您需要复制
value_if_empty
,默认对其进行构造,或者将其替换为工厂函数,该工厂函数将返回新值(如果为空)。我最喜欢的解决方案是默认构造它。
尽管如此,这里还是带有工厂功能的示例:
template <typename T>
class AtomicQueue {
std::function<T()> make_empty_value;
public:
T pop()
{
T a = make_empty_value();
// ...
}
};
然后在创建它时将其传递给您的类(class):
AtomicQueue<std::unique_ptr<Class1>> B([]{ return std::unique_ptr<Class1>{nullptr}; });
如果要避免
std::function
的开销,则可以用lambda类型的模板参数替换成员变量。
关于c++ - 一对使用unique_ptr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60321167/