并捕获通过在C ++ lambda表达式值所需要的值以与拉姆达对象被复制?(Does capture

2019-09-16 10:44发布

是否标准定义与此代码会发生什么?

#include <iostream>

template <typename Func>
void callfunc(Func f)
{
   ::std::cout << "In callfunc.\n";
    f();
}

template <typename Func>
void callfuncref(Func &f)
{
   ::std::cout << "In callfuncref.\n";
    f();
}

int main()
{
    int n = 10;
    // n is captured by value, and the lambda expression is mutable so
    // modifications to n are allowed inside the lambda block.
    auto foo = [n]() mutable -> void {
       ::std::cout << "Before increment n == " << n << '\n';
       ++n;
       ::std::cout << "After increment n == " << n << '\n';
    };
    callfunc(foo);
    callfunc(foo);
    callfuncref(foo);
    callfunc(foo);
    return 0;
}

这与克++的输出是:

$ ./a.out 
In callfunc.
Before increment n == 10
After increment n == 11
In callfunc.
Before increment n == 10
After increment n == 11
In callfuncref.
Before increment n == 10
After increment n == 11
In callfunc.
Before increment n == 11
After increment n == 12

是否需要通过标准的此输出的所有功能?

特别地,它看来,如果拉姆达对象的副本由所有所捕获的值也被复制。 但是,如果对象拉姆达通过引用无捕获的值的传递被复制。 而且无份由函数被调用之前捕捉的值,因此被保留,否则调用之间的突变,以捕获值。

Answer 1:

类型拉姆达的是一个简单的类(n3290§5.1.2/ 3),与operator()其执行所述主体(/ 5),并且隐式拷贝构造(/ 19),并通过复制捕获变量是相当于它复制初始化(/ 21)到这个类的非静态数据成员(/ 14),和该变量的每次使用由相应数据部件(/ 17)代替。 此改造后的λ表达式变成只有这个类的一个实例,并遵循C ++的一般规则。

这意味着,你的代码将在方法一样工作:

int main()
{
    int n = 10;

    class __Foo__           // §5.1.2/3
    {
        int __n__;          // §5.1.2/14
    public:
        void operator()()   // §5.1.2/5
        {
            std::cout << "Before increment n == " << __n__ << '\n';
            ++ __n__;       // §5.1.2/17
            std::cout << "After increment n == " << __n__ << '\n';
        }
        __Foo__() = delete;
        __Foo__(int n) : __n__(n) {}
      //__Foo__(const __Foo__&) = default;  // §5.1.2/19
    }
    foo {n};                // §5.1.2/21

    callfunc(foo);
    callfunc(foo);
    callfuncref(foo);
    callfunc(foo);
}

它是什么明显callfuncref这里呢。



Answer 2:

我觉得最简单的由氧手动扩展到一个struct /类,这或多或少是这样理解这个行为(如n是按值捕获,捕获引用看起来有点不同):

class SomeTemp {
    mutable int n;
    public:
    SomeTemp(int n) : n(n) {}
    void operator()() const
    {
       ::std::cout << "Before increment n == " << n << '\n';
       ++n;
       ::std::cout << "After increment n == " << n << '\n';
    }
} foo(n);

你的功能callfunccallfuncref操作或多或少地对这个类型的对象。 现在,让我们看看你做的电话:

callfunc(foo);

在这里,您按值传递,所以foo将使用默认的拷贝构造函数进行复制。 在操作callfunc只会影响到复制的值的内部状态,没有一个国家在实际Foo对象改变。

callfunc(foo);

同样的东西

callfuncref(foo);

啊,我们在这里通过引用传递foo的,所以callfuncref (这就要求operator()将更新的实际foo对象,而不是一个临时副本。 这将导致nfoo被更新为11后,这是常规的pass-by-reference的行为。 因此,当您再次调用此:

callfunc(foo);

您将在副本上再次运行,但副本foo其中n被设置为11这表明你的期望。



Answer 3:

该值由拷贝捕获除非明确捕捉-所有[&]或通过引用捕获特定变量[&n] 所以整个输出为标准。



文章来源: Does capture by value in a C++ lambda expression require the value to be copied with the lambda object?
标签: c++ lambda c++11