我正在尝试以一种既可以使用仅移动对象也可以使用仅复制对象的方式实现通用线程安全队列。这是我尝试过的(为了简单起见,我删除了所有不相关的代码(锁)):
struct MoveOnly
{
MoveOnly() = default;
MoveOnly(const MoveOnly& a) = delete;
MoveOnly& operator=(const MoveOnly& a) = delete;
MoveOnly(MoveOnly&& a) = default;
MoveOnly& operator=(MoveOnly&& a) = default;
std::vector<int> v;
};
struct CopyOnly
{
CopyOnly() = default;
CopyOnly(const CopyOnly &a) = default;
CopyOnly &operator=(const CopyOnly &a) = default;
CopyOnly(CopyOnly &&a) = delete;
CopyOnly &operator=(CopyOnly &&a) = delete;
std::vector<int> v;
};
template <typename T>
class Queue
{
std::queue<T> q;
public:
T pop()
{
T t = q.front();
return t;
}
void push(T&& t)
{
q.push(std::forward<T>(t));
}
};
int main()
{
Queue<MoveOnly> qm;
qm.push(MoveOnly());
MoveOnly mo = qm.pop();
Queue<CopyOnly> qc;
CopyOnly c;
qc.push(c);
CopyOnly&& co = qc.pop();
}
Run Code Online (Sandbox Code Playgroud)
由于函数返回左值引用,因此存在多个编译错误pop:T t = q.front()无法使用移动语义。 T t = std::move(q.front())不适用于显式删除的移动构造函数,因为运算符重载将解析为已删除的构造函数。以前push函数中也出现过同样的问题,但是通过完美转发解决了。
另一个问题显然return t与 的移动构造函数绑定CopyOnly,我很难理解为什么会这样。
有没有办法pop同时使用MoveOnly和CopyOnly对象?
CopyOnly附带问题:像显式删除的移动构造函数一样定义对象是否有意义?在哪种情况下这样做会有用?因为如果隐式删除构造函数,它会起作用。
您可以使用constexpr if并检查 if Tis std::move_constructible_v。
我还会创建一个emplace代理:
#include <type_traits>
template<typename T>
class Queue {
std::queue<T> q;
public:
decltype(auto) pop() {
if constexpr(std::is_move_constructible_v<T>) {
T t = std::move(q.front());
q.pop();
return t;
} else {
T t = q.front();
q.pop();
return t;
}
}
template<class... Args>
decltype(auto) emplace(Args&&... args) {
return q.emplace(std::forward<Args>(args)...);
}
};
Run Code Online (Sandbox Code Playgroud)
这是一个 C++11 版本(我之前没有注意到 C++11 标签)。删除移动构造函数和移动赋值运算符CopyOnly真的把它弄得一团糟。您可能永远不应该在实际代码中这样做。
要开始CopyOnly co = qc.pop();工作,pop()需要返回 aconst T否则移动构造函数将成为重载决议的一部分,即使它被删除,它仍然是重载决议的一部分,但编译将因为它被删除而失败。
如果CopyOnly&& co = qc.pop();是对你合适,你可以更换const U同U在enable_if。
template<typename T>
class Queue {
std::queue<T> q{};
public:
template<typename U = T>
typename std::enable_if<std::is_move_constructible<U>::value, U>::type
pop() {
U t = std::move(q.front());
q.pop();
return t;
}
template<typename U = T>
typename std::enable_if<!std::is_move_constructible<U>::value, const U>::type
pop() {
U t = q.front();
q.pop();
return t;
}
template<class... Args>
void emplace(Args&&... args) {
q.emplace(std::forward<Args>(args)...);
}
};
Run Code Online (Sandbox Code Playgroud)
这是另一个基于 rafix07 的想法构建的 C++11 版本,它带有一个额外的popper类型,它执行popafter 返回以处理 gcc 7.3 中可能存在的错误。
template<typename T>
class Queue {
std::queue<T> q{};
struct popper {
std::queue<T>& q_ref;
~popper() {
q_ref.pop();
}
};
public:
using Type = typename
std::conditional<std::is_move_constructible<T>::value, T, T&>::type;
T pop() {
popper pop_after_return{q};
return std::forward<Type>(q.front());
}
template<class... Args>
void emplace(Args&&... args) {
q.emplace(std::forward<Args>(args)...);
}
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
99 次 |
| 最近记录: |