对于C ++ 11中的Packaged_Task实现 我想实现我在C ++ 14 Code中所表达的内容。换句话说,我想转发一个lambda表达式。
template<class F>
Packaged_Task(F&& f) {
Promise<R> p;
_future = p.get_future();
auto f_holder = [f = std::forward<F>(f)]() mutable { return std::move(f); };
///...
我知道进入lambda的变通方法(但是这个变通方法需要一个默认的可构造对象,在我的例子中,对象通常是没有默认构造函数的lambda表达式)
答案 0 :(得分:3)
如何创建一个在复制构造过程中移动的包装结构:(。(我知道这很糟糕,让我记住auto_ptr
)
template <typename F>
struct Wrapped {
using Ftype = typename std::remove_reference<F>::type;
Wrapped(Ftype&& f): f_(std::move(f)) {}
Wrapped(const Wrapped& o): f_(std::move(o.f_)) {}
mutable Ftype f_;
};
template<class F>
Packaged_Task(F&& f) {
Promise<R> p;
_future = p.get_future();
Wrapped<std::remove_reference<decltype(f)>::type> wrap(std::forward<F>(f));
auto f_holder = [wrap]() mutable { return std::move(wrap.f_); };
这只是一个粗略的想法。未编译或测试。
注意:我之前看过这种技术,不记得它是在SO本身还是在博客上。
答案 1 :(得分:3)
首先,让我们把问题归结为它的核心:函数对象有点分散注意力。从本质上讲,您希望能够创建一个带有捕获的lambda,该捕获包含一个只移动的对象。在C ++ 11中,没有直接支持,这引起了C ++ 14方法的允许规范如何构建捕获。
对于C ++ 11,有必要使用副本。由于底层类型不支持复制,因此有必要实际移动对象而不是复制它。这样做可以通过合适的包装器来实现,该包装器定义了一个不是真正复制而是移动的复制构造函数。以下示例显示:
#include <utility>
struct foo {
foo(int) {}
foo(foo&&) = default;
foo(foo const&) = delete;
};
template <typename T>
class move_copy
{
T object;
public:
move_copy(T&& object): object(std::move(object)) {}
move_copy(move_copy& other): object(std::move(other.object)) {}
T extract() { return std::forward<T>(this->object); }
};
template <typename T>
void package(T&& object)
{
move_copy<T> mc(std::forward<T>(object));
foo g = [mc]() mutable { return mc.extract(); }();
}
int main()
{
foo f(0);
package(std::move(f));
}
move_copy<T>
包装器实际上只是以传递方式捕获参数:如果传入左值,则捕获左值。为了正确获取所包含的对象,extract()
成员std::forward<T>()
是对象:该函数只能安全地调用一次,因为对象可能会从那里移动。
答案 2 :(得分:1)
通过使它成为一个移动来打破复制语义是一个坏主意。如果这是唯一的选择,那就去吧,但事实并非如此。
相反,我们可以将移动的值作为参数传递给lambda,并将其移动到包装代码中。
curry_apply
获取一些值和一个函数对象,并返回该值与第一个参数绑定的值的函数对象。
template<class T, class F>
struct curry_apply_t {
T t;
F f;
template<class...Args>
auto operator()(Args&&...args)
-> typename std::result_of_t<F&(T&, Args...)>::type
{
return f(t, std::forward<Args>(args)...);
}
};
template<class T, class F>
curry_apply_t<typename std::decay<T>::type, typename std::decay<F>::type>
curry_apply( T&& t, F&& f ) {
return {std::forward<T>(t), std::forward<F>(f)};
}
使用:
template<class F>
Packaged_Task(F&& f) {
Promise<R> p;
_future = p.get_future();
auto f_holder = curry_apply(
std::move(_future),
[](Future<R>& f) mutable { return std::move(f); };
);
基本上我们将移植的数据存储在lambda的中,并将其存储在手动编写的函数对象中。然后我们将它作为一个左值参数传递给lambda参数列表的前面。
Here是同一解决方案的更复杂版本。