lambda 函数对象中的静态变量如何工作?
lambda 中使用的静态变量是否在使用 lambda 的函数的调用中保留?还是每次函数调用都会重新创建"这个函数对象?
Are static variables used in a lambda retained across calls of the function wherein the lambda is used? Or is the function object "created" again each function call?
无用示例:
#include <iostream>
#include <vector>
#include <algorithm>
using std::cout;
void some_function()
{
std::vector<int> v = {0,1,2,3,4,5};
std::for_each( v.begin(), v.end(),
[](const int &i)
{
static int calls_to_cout = 0;
cout << "cout has been called " << calls_to_cout << " times.
"
<< " Current int: " << i << "
";
++calls_to_cout;
} );
}
int main()
{
some_function();
some_function();
}
这个程序的正确输出是什么?它是否取决于 lambda 是否捕获局部变量的事实?(它肯定会改变函数对象的底层实现,所以它可能会产生影响)是否允许行为不一致?
What is the correct output for this program? Is it dependent on the fact if the lambda captures local variables or not? (it will certainly change the underlying implementation of the function object, so it might have an influence) Is it an allowed behavioural inconsistency?
我不是在寻找:我的编译器输出......",恕我直言,这是一个太新的功能,无法信任当前的实现.我知道要求标准报价似乎很受欢迎,因为世界发现存在这样的事情,但仍然,我想要一个像样的来源.
I'm not looking for: "My compiler outputs ...", this is too new a feature to trust current implementations IMHO. I know asking for Standard quotes seems to be popular since the world discovered such a thing exists, but still, I would like a decent source.
推荐答案
tl;dr 版本在底部.
tl;dr version at the bottom.
§5.1.2 [expr.prim.lambda]
p1 lambda 表达式:
lambda-introducer lambda-declaratoropt 复合语句
p1 lambda-expression:
lambda-introducer lambda-declaratoropt compound-statement
p3 lambda 表达式的类型(也是闭包对象的类型)是一个唯一的、未命名的非联合类类型――称为闭包类型――其属性如下所述.这个类类型不是聚合(8.5.1).闭包类型在包含相应lambda 表达式的最小块作用域、类作用域或命名空间作用域中声明.(我的笔记:函数有一个块作用域.)
p3 The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type ― called the closure type ― whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. (My note: Functions have a block scope.)
p5 lambda 表达式的闭包类型有一个公共的inline
函数调用操作符[...]
p5 The closure type for a lambda-expression has a public inline
function call operator [...]
p7 lambda 表达式的复合语句产生函数调用运算符 [...]
p7 The lambda-expression’s compound-statement yields the function-body (8.4) of the function call operator [...]
由于复合语句直接作为函数调用操作符的主体,并且闭包类型定义在最小(最里面)的作用域中,所以和下面的写法是一样的:
Since the compound-statement is directly taken as the function call operator's body, and the closure type is defined in the smallest (innermost) scope, it's the same as writing the following:
void some_function()
{
struct /*unnamed unique*/{
inline void operator()(int const& i) const{
static int calls_to_cout = 0;
cout << "cout has been called " << calls_to_cout << " times.
"
<< " Current int: " << i << "
";
++calls_to_cout;
}
} lambda;
std::vector<int> v = {0,1,2,3,4,5};
std::for_each( v.begin(), v.end(), lambda);
}
哪个是合法的 C++,允许函数有 static
局部变量.
Which is legal C++, functions are allowed to have static
local variables.
§3.7.1 [basic.stc.static]
p1 所有没有动态存储期、没有线程存储期、非局部变量都有静态存储期.这些实体的存储将在计划期间持续.
p1 All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program.
p3 关键字static
可用于声明具有静态存储期的局部变量.[...]
p3 The keyword static
can be used to declare a local variable with static storage duration. [...]
§6.7 [stmt.dcl] p4
(这涉及块范围内具有静态存储持续时间的变量的初始化.)
§6.7 [stmt.dcl] p4
(This deals with initialization of variables with static storage duration in a block scope.)
[...] 否则这样的变量在第一次控制通过它的声明时被初始化;[...]
[...] Otherwise such a variable is initialized the first time control passes through its declaration; [...]
<小时>
重申:
- lambda 表达式的类型是在最内层作用域中创建的.
- 它不是为每个函数调用重新创建(这是没有意义的,因为封闭的函数体就像我上面的例子).
- 它遵守(几乎)所有普通类/结构的规则(只是关于
this
的一些内容不同),因为它是非联合类类型.
- The type of a lambda expression is created in the innermost scope.
- It is not created anew for each function call (that wouldn't make sense, since the enclosing function body would be as my example above).
- It obeys (nearly) all the rules of normal classes / structs (just some stuff about
this
is different), since it is a non-union class type.
既然我们已经保证对于每个函数调用,闭包类型都是一样的,我们可以有把握地说静态局部变量也是一样的;它在第一次调用函数调用运算符时被初始化,并一直存在到程序结束.
Now that we have assured that for every function call, the closure type is the same, we can safely say that the static local variable is also the same; it's initialized the first time the function call operator is invoked and lives until the end of the program.
相关文章