在 lambda 中移动捕获 [英] Move capture in lambda

查看:57
本文介绍了在 lambda 中移动捕获的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在 C++11 lambda 中通过移动(也称为右值引用)进行捕获?

How do I capture by move (also known as rvalue reference) in a C++11 lambda?

我正在尝试写这样的东西:

I am trying to write something like this:

std::unique_ptr<int> myPointer(new int);

std::function<void(void)> example = [std::move(myPointer)]{
   *myPointer = 4;
};

推荐答案

C++14 中的广义 lambda 捕获

在 C++14 中,我们将有所谓的 广义 lambda 捕获.这将启用移动捕获.以下将是 C++14 中的合法代码:

Generalized lambda capture in C++14

In C++14 we will have the so called generalized lambda capture. This enables move capture. The following will be legal code in C++14:

using namespace std;

// a unique_ptr is move-only
auto u = make_unique<some_type>( some, parameters );  

// move the unique_ptr into the lambda
go.run( [ u = move(u) ] { do_something_with( u ); } ); 

但从某种意义上说,捕获的变量可以用任何类似的东西初始化:

But it is much more general in the sense that captured variables can be initialized with anything like so:

auto lambda = [value = 0] mutable { return ++value; };

在 C++11 中这是不可能的,但有一些涉及辅助类型的技巧.幸运的是,Clang 3.4 编译器已经实现了这个很棒的功能.如果保持最近的发布速度,编译器将在 2013 年 12 月或 2014 年 1 月发布.

In C++11 this is not possible yet, but with some tricks that involve helper types. Fortunately, the Clang 3.4 compiler already implements this awesome feature. The compiler will be released December 2013 or January 2014, if the recent release pace will be kept.

更新:Clang 3.4 编译器 于 2014 年 1 月 6 日发布,具有上述功能.

UPDATE: The Clang 3.4 compiler was released on 6 Jan 2014 with the said feature.

这是一个辅助函数 make_rref 的实现,它有助于人为的移动捕获

Here's an implementation of a helper function make_rref which helps with artificial move capture

#include <cassert>
#include <memory>
#include <utility>

template <typename T>
struct rref_impl
{
    rref_impl() = delete;
    rref_impl( T && x ) : x{std::move(x)} {}
    rref_impl( rref_impl & other )
        : x{std::move(other.x)}, isCopied{true}
    {
        assert( other.isCopied == false );
    }
    rref_impl( rref_impl && other )
        : x{std::move(other.x)}, isCopied{std::move(other.isCopied)}
    {
    }
    rref_impl & operator=( rref_impl other ) = delete;
    T && move()
    {
        return std::move(x);
    }

private:
    T x;
    bool isCopied = false;
};

template<typename T> rref_impl<T> make_rref( T && x )
{
    return rref_impl<T>{ std::move(x) };
}

这是该函数在我的 gcc 4.7.3 上成功运行的测试用例.

And here's a test case for that function that ran successfully on my gcc 4.7.3.

int main()
{
    std::unique_ptr<int> p{new int(0)};
    auto rref = make_rref( std::move(p) );
    auto lambda =
        [rref]() mutable -> std::unique_ptr<int> { return rref.move(); };
    assert(  lambda() );
    assert( !lambda() );
}

这里的缺点是 lambda 是可复制的,当复制 rref_impl 的复制构造函数中的断言时,会导致运行时错误.以下可能是更好、更通用的解决方案,因为编译器会捕获错误.

The drawback here is that lambda is copyable and when copied the assertion in the copy constructor of rref_impl fails leading to a runtime bug. The following might be a better and even more generic solution because the compiler will catch the error.

这里还有一个想法,关于如何实现广义 lambda 捕获.函数 capture() 的使用(其实现可以在下面找到)如下:

Here's one more idea, on how to implement generalized lambda capture. The use of the function capture() (whose implementation is found further down) is as follows:

#include <cassert>
#include <memory>

int main()
{
    std::unique_ptr<int> p{new int(0)};
    auto lambda = capture( std::move(p),
        []( std::unique_ptr<int> & p ) { return std::move(p); } );
    assert(  lambda() );
    assert( !lambda() );
}

这里的 lambda 是一个函子对象(几乎是一个真正的 lambda),它在传递给 capture() 时捕获了 std::move(p).capture 的第二个参数是一个 lambda,它将捕获的变量作为参数.当 lambda 用作函数对象时,传递给它的所有参数都将在捕获的变量之后作为参数转发到内部 lambda.(在我们的例子中,没有进一步的论据要转发).本质上,与之前的解决方案相同.以下是 capture 的实现方式:

Here lambda is a functor object (almost a real lambda) which has captured std::move(p) as it is passed to capture(). The second argument of capture is a lambda which takes the captured variable as an argument. When lambda is used as a function object, then all arguments that are passed to it will be forwarded to the internal lambda as arguments after the captured variable. (In our case there are no further arguments to be forwarded). Essentially, the same as in the previous solution happens. Here's how capture is implemented:

#include <utility>

template <typename T, typename F>
class capture_impl
{
    T x;
    F f;
public:
    capture_impl( T && x, F && f )
        : x{std::forward<T>(x)}, f{std::forward<F>(f)}
    {}

    template <typename ...Ts> auto operator()( Ts&&...args )
        -> decltype(f( x, std::forward<Ts>(args)... ))
    {
        return f( x, std::forward<Ts>(args)... );
    }

    template <typename ...Ts> auto operator()( Ts&&...args ) const
        -> decltype(f( x, std::forward<Ts>(args)... ))
    {
        return f( x, std::forward<Ts>(args)... );
    }
};

template <typename T, typename F>
capture_impl<T,F> capture( T && x, F && f )
{
    return capture_impl<T,F>(
        std::forward<T>(x), std::forward<F>(f) );
}

第二个解决方案也更简洁,因为如果捕获的类型不可复制,它会禁止复制 lambda.在第一个只能在运行时使用 assert() 进行检查的解决方案中.

This second solution is also cleaner, because it disables copying the lambda, if the captured type is not copyable. In the first solution that can only be checked at runtime with an assert().

这篇关于在 lambda 中移动捕获的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆