C++14 中泛型 Lambda 中的静态自动变量
Static Auto Variable in Generic Lambda in C++14
class A {
public:
int a;
char b;
double c;
A ( int x, char y, double z ) : a(x), b(y), c(z){}
};
int main(){
auto lambda = []( auto x ) {
static auto y = x;
// y = x;
return y;
};
int a = lambda(1);
char b = lambda('a');
double c = lambda(1.5);
A d = lambda( A( 2, 'b', 2.5 ) );
return 0;
}
此代码在 Clang 3.8.0 和 GCC 5.4.0 中编译,并且运行良好。但是,考虑到变量 y
是 static
:
- 变量的类型是什么
y
? y
的类型是否会在每次调用 lambda 时发生变化?
- 变量
y
是否在每次调用时都被初始化,尽管它是 static
?注释赋值 // y = x
不需要更新变量 y
. 的值
- 此行为是否符合 C++14 标准?
如果我在每次调用中打印 sizeof(y)
,我将分别得到 4、1、8 和 16。
On local and global static variables in C++
你的 lambda 是通用的。这意味着这是一个伪装的模板。根据模板特化的一般规则处理这种情况。
对于每个特定的推导类型的参数 x
,您将获得函数的单独特化。因此,是的,对于每个特定类型的 x
,您都会得到一个单独的 y
副本。但是底层机制并没有以某种方式本地化在您的 static
中,而是您的函数的整个主体 "copied" 产生了该函数的单独独立实现。
你的 lambda 的每个特化都有一个单独的 y
副本,它只会在第一次调用该特定特化时初始化一次。
情况实际上等同于更明确的
template <typename T> void foo(T x)
{
static T y = x;
std::cout << y << std::endl;
}
int main()
{
foo(1);
foo('a');
foo(1.5);
foo(3.0);
}
输出 1
、a
、1.5
和 1.5
。
在此示例中,您获得了 foo
的三个独立特化:foo<int>
、foo<char>
和 foo<double>
。 foo
的每个版本都有自己的 y
版本,这意味着在此示例中有三个不同的静态 y
。对每个特化的第一次调用将初始化 y
,后续调用将不会重新初始化它。在这种情况下,对 foo(1.5)
的调用会为 foo<double>
初始化 y
,但对 foo(3.0)
的后续调用不会。
同样的事情也发生在你的案例中,只是使用了不同的语法。
Lamda with auto 无非是class operator() 重载为模板,使用auto 进行类型推导(遵循模板参数推导规则)
在这种情况下,您拥有与此函数对象模板运算符的实例化一样多的静态 y 字段。
初始化就像手写的那样完成 class 这是第一次触发函数(在这种情况下是 lambda)
class A {
public:
int a;
char b;
double c;
A ( int x, char y, double z ) : a(x), b(y), c(z){}
};
int main(){
auto lambda = []( auto x ) {
static auto y = x;
// y = x;
return y;
};
int a = lambda(1);
char b = lambda('a');
double c = lambda(1.5);
A d = lambda( A( 2, 'b', 2.5 ) );
return 0;
}
此代码在 Clang 3.8.0 和 GCC 5.4.0 中编译,并且运行良好。但是,考虑到变量 y
是 static
:
- 变量的类型是什么
y
?y
的类型是否会在每次调用 lambda 时发生变化? - 变量
y
是否在每次调用时都被初始化,尽管它是static
?注释赋值// y = x
不需要更新变量y
. 的值
- 此行为是否符合 C++14 标准?
如果我在每次调用中打印 sizeof(y)
,我将分别得到 4、1、8 和 16。
On local and global static variables in C++
你的 lambda 是通用的。这意味着这是一个伪装的模板。根据模板特化的一般规则处理这种情况。
对于每个特定的推导类型的参数 x
,您将获得函数的单独特化。因此,是的,对于每个特定类型的 x
,您都会得到一个单独的 y
副本。但是底层机制并没有以某种方式本地化在您的 static
中,而是您的函数的整个主体 "copied" 产生了该函数的单独独立实现。
你的 lambda 的每个特化都有一个单独的 y
副本,它只会在第一次调用该特定特化时初始化一次。
情况实际上等同于更明确的
template <typename T> void foo(T x)
{
static T y = x;
std::cout << y << std::endl;
}
int main()
{
foo(1);
foo('a');
foo(1.5);
foo(3.0);
}
输出 1
、a
、1.5
和 1.5
。
在此示例中,您获得了 foo
的三个独立特化:foo<int>
、foo<char>
和 foo<double>
。 foo
的每个版本都有自己的 y
版本,这意味着在此示例中有三个不同的静态 y
。对每个特化的第一次调用将初始化 y
,后续调用将不会重新初始化它。在这种情况下,对 foo(1.5)
的调用会为 foo<double>
初始化 y
,但对 foo(3.0)
的后续调用不会。
同样的事情也发生在你的案例中,只是使用了不同的语法。
Lamda with auto 无非是class operator() 重载为模板,使用auto 进行类型推导(遵循模板参数推导规则)
在这种情况下,您拥有与此函数对象模板运算符的实例化一样多的静态 y 字段。
初始化就像手写的那样完成 class 这是第一次触发函数(在这种情况下是 lambda)