gpt4 book ai didi

c++ - 有没有办法在模板中透明地使用移动或复制构造函数?

转载 作者:行者123 更新时间:2023-12-01 22:43:41 24 4
gpt4 key购买 nike

我正在尝试以一种可以与仅移动对象或仅复制对象一起使用的方式实现通用线程安全队列。这是我尝试过的(为了简单起见,我删除了所有不相关的代码(锁)):

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();
}

由于 pop,存在多个编译错误:T t = q.front() 无法使用移动语义,因为函数返回左值引用。 T t = std::move(q.front()) 不适用于显式删除的移动构造函数,因为运算符重载将解析为已删除的构造函数。同样的问题曾经出现在push功能中,但通过完美转发解决了。

另一个问题显然是 return t 绑定(bind)到 CopyOnly 的移动构造函数,我很难理解为什么这样做。

有没有办法让 popMoveOnlyCopyOnly 对象一起使用?

<小时/>

附带问题:使用显式删除的移动构造函数定义像 CopyOnly 这样的对象是否有意义?在什么情况下这样做会有用?因为如果隐式删除构造函数,它就会起作用。

最佳答案

您可以使用 constexpr if 并检查 T 是否为 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)...);
}
};
<小时/>

这是一个C++11版本(我之前没有注意到C++11标签)。在 CopyOnly 中删除移动构造函数和移动赋值运算符确实让事情变得一团糟。您可能永远不应该在实际代码中这样做。

要使 CopyOnly co = qc.pop(); 正常工作,pop() 需要返回一个 const 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)...);
}
};
<小时/>

这是另一个基于 rafix07 的想法构建的 C++11 版本,带有额外的 popper 类型,该类型在返回后执行 pop 以处理 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)...);
}
};

关于c++ - 有没有办法在模板中透明地使用移动或复制构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61497760/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com