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

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

问题描述

如何在C ++ 11 lambda中移动(也称为右值引用)?

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

我试图写这样: p>

I am trying to write something like this:

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

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


推荐答案

C ++ 14中的通用lambda捕获



在C ++ 14中,我们将使用所谓的广义拉姆达捕获。这使能移动捕获。下面是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 ); } ); 

但是更为一般的意义是捕获的变量可以用如下方式初始化: / p>

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.

这里有一个想法,捕获。使用函数 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),捕获 std :: move(p),因为它传递给 capture $ c>。 capture 的第二个参数是一个lambda,它将捕获的变量作为参数。当将 lambda 用作函数对象时,传递给它的所有参数将被转发到内部lambda作为捕获的变量后的参数。 (在我们的情况下,没有进一步的参数被转发)。基本上,与以前的解决方案相同。 捕获的实现方式如下:

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天全站免登陆