我正在尝试构建一个可调用对象链,以后可以异步执行。我想尝试以下方法:构建节点的“嵌套”结构(通过将每个节点移动到其“父”),从而生成存储所有计算的对象,并且可以启动链按需。
这就是我的想法:
template <typename TParent, typename TF>
struct node
{
TParent _parent;
TF _f;
node(TParent&& parent, TF&& f)
: _parent{std::move(parent)}, _f{std::move(f)}
{
}
template <typename TFContinuation>
auto then(TFContinuation&& f_continuation)
{
using this_type = node<TParent, TF>;
return node<this_type, std::decay_t<TFContinuation>>
{std::move(*this), std::move(f_continuation)};
// ^^^^^^^^^^^^^^^^
// ...safe?
}
};
上面的代码允许用户编写如下链:
int main()
{
node n{some_root_callable, []{/*...*/}};
n.then([]{/*...*/})
.then([]{/*...*/})
.then([]{/*...*/})
.then([]{/*...*/});
}
(真正的实现将支持更有用的抽象,例如when_all(...)
或when_any(...)
。)
假设TParent
,TF
和TFContinuation
是可移动的可调用对象,是安全的(即明确定义的)来调用{在调用std::move(*this)
?
答案 0 :(得分:3)
你可以做到这一点并且安全。它只会在大多数情况下中将成员保留在未定义但有效的状态中。话虽如此,只要您不再尝试使用其成员,移动this
是安全的。但是对于标准库类型和大多数用户定义的类型,这甚至都不成问题。
有一件事我会改变。我只允许来自rvalue的电话:
template <typename TFContinuation> // v-- notice the && here.
auto then(TFContinuation&& f_continuation) && {
using this_type = node<TParent, TF>;
return node<this_type, std::decay_t<TFContinuation>>{
std::move(*this), std::move(f_continuation)
};
}
很棒的是,当它不是右值时,你甚至可以超载它:
template <typename TFContinuation>
auto then(TFContinuation&& f_continuation) const & {
using this_type = node<TParent, TF>;
return node<this_type, std::decay_t<TFContinuation>>{
*this, std::move(f_continuation)
};
}
答案 1 :(得分:0)
该代码中是否存在问题取决于该代码对其获得的引用的作用。如果被调用的代码将对象变为糊状,那么当它返回时,你的代码必须处理一个被变成糊状的对象。但是对于从成员函数调用的任何函数来说都是如此,无论它是使用rvalue引用,可修改的左值引用,指针还是您可能想要的任何其他机制调用。 / p>