2016-11-15 50 views
3

我想构建一个可调用的对象链,以后可以异步执行。我想尝试以下方法:构建节点的“嵌套”结构(通过将每个节点移动到其“父”中)导致存储所有计算的对象,并且可以按需启动链。将`std :: move(* this)`安全到`this-> some_method`创建的对象中吗?

这是我脑子里想的:

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(...)。)

Wandbox example


假设TParentTF,和TFContinuation是可移动的可调用的对象,是安全(即明确定义)到的node::then调用期间调用std::move(*this)

+0

这真的取决于你将如何使用'*此搬家后。如果你没有破坏任何东西(也就是说,将对象的状态保留在析构函数无法处理的状态),我看不到问题。 –

+0

'std :: move'不会移动任何东西,所以在_move_之后你可以做什么主要取决于'TParent'的构造函数实际上做了什么。也就是说,你可以删除'this'指针并且仍然可以避开它。只要你没有玩过_move_之后剩下的东西,我看不出有什么理由不能做到这一点。 – skypjack

回答

3

你可以做到这一点,它是安全的。它只会让成员未定义但有效的状态大多数情况下为。有了这个说法,只要你不再尝试使用它的成员,移动this是安全的。但是对于标准库类型和大多数用户定义的类型,这甚至不会成为问题。

有一件事我会改变。我将只允许右值这个调用:

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) 
    }; 
} 
+0

回复:“让成员处于未定义但有效的状态” - 对于标准库中定义的所有类型**(这些词来自哪里)都是如此,但不能保证用户定义的类型将会执行这个。所以“这是安全的”太强大了。 –

+0

我会编辑我的答案,谢谢指出 –

0

代码中是否存在问题取决于代码对引用的操作。如果被调用的代码将对象变成糊状,那么当它返回时,你的代码必须处理已变成糊状的对象。但是,您可以从成员函数调用的函数是真实的,无论它是否用右值引用,可修改的左值引用,指针或您想要设想的任何其他机制调用。

+0

从'std :: move(* this)'返回的rvalue-ref作为新构造的'node'对象的'parent'参数传递,以便将“父节点”存储在新节点的'_parent '领域。我们的想法是将异步计算的节点递归存储到彼此中,以便构建一个“大”对象,该对象包含稍后开始执行异步链所需的所有数据。 –

+0

之后,遍历层次结构,并通过递归调用所有存储对象的execute()方法来构建异步计算。我有一个[非工作实现](http://melpon.org/wandbox/permlink/HfrjVkob1QP476QT),它在g ++ *(但不是在clang ++)*上的段错误。我一直在试图理解UB在哪里一段时间,'std :: move(* this)'看起来像一个“红旗”。 –