是否标准定义与此代码会发生什么?
#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
是否需要通过标准的此输出的所有功能?
特别地,它看来,如果拉姆达对象的副本由所有所捕获的值也被复制。 但是,如果对象拉姆达通过引用无捕获的值的传递被复制。 而且无份由函数被调用之前捕捉的值,因此被保留,否则调用之间的突变,以捕获值。
类型拉姆达的是一个简单的类(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
这里呢。
我觉得最简单的由氧手动扩展到一个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);
你的功能callfunc
和callfuncref
操作或多或少地对这个类型的对象。 现在,让我们看看你做的电话:
callfunc(foo);
在这里,您按值传递,所以foo
将使用默认的拷贝构造函数进行复制。 在操作callfunc
只会影响到复制的值的内部状态,没有一个国家在实际Foo对象改变。
callfunc(foo);
同样的东西
callfuncref(foo);
啊,我们在这里通过引用传递foo的,所以callfuncref
(这就要求operator()
将更新的实际foo
对象,而不是一个临时副本。 这将导致n
的foo
被更新为11
后,这是常规的pass-by-reference
的行为。 因此,当您再次调用此:
callfunc(foo);
您将在副本上再次运行,但副本foo
其中n
被设置为11这表明你的期望。
该值由拷贝捕获除非明确捕捉-所有[&]
或通过引用捕获特定变量[&n]
所以整个输出为标准。
文章来源: Does capture by value in a C++ lambda expression require the value to be copied with the lambda object?