Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 编译器是否为每个lambda生成不同的类型?_C++_Templates_Lambda_C++14 - Fatal编程技术网

C++ 编译器是否为每个lambda生成不同的类型?

C++ 编译器是否为每个lambda生成不同的类型?,c++,templates,lambda,c++14,C++,Templates,Lambda,C++14,免责声明:请勿在此问题中使用代码。它调用未定义的行为。问题的核心陈述,编译器是否为每个lambda生成新类型,以及相应的答案仍然有效 为了使用捕获获取指向lambda的函数指针,我想出了以下技巧: auto f = [&a] (double x) { return a*x; }; static auto proxy = f; double(*ptr)(double) = [] (double x) { return proxy(x); }; // do something with pt

免责声明:请勿在此问题中使用代码。它调用未定义的行为。问题的核心陈述,编译器是否为每个lambda生成新类型,以及相应的答案仍然有效

为了使用捕获获取指向lambda的函数指针,我想出了以下技巧:

auto f = [&a] (double x) { return a*x; };
static auto proxy = f;
double(*ptr)(double) = [] (double x) { return proxy(x); };
// do something with ptr
我将带有捕获(可能是函数参数)的lambda分配给函数中的静态变量,因此在其他lambda中使用它时不必捕获它。另一个无CaptureLambda可以很高兴地衰减为一个函数指针,我可以将它传递给某个共享库

现在,我们可以尝试概括这一点:

template < typename F >
decltype(auto) get_ptr(F f)
{
  static auto proxy = f;
  return [] (auto ... args) { return proxy(args...); };
}
对于
get_ptr(f1)
get_ptr(f5)
来说,这看起来很可疑,人们可能期望推断出相同的类型。但是,lambda是编译器生成的结构,似乎编译器会为每个lambda生成不同的类型,而不管以前的lambda看起来是否可以重用

因此,在编译器肯定会为每个lambda生成不同类型的情况下,上述技巧对我非常有用。如果不是这样的话,我的hack的泛化是无用的。

来自规范草案(特别是n3376),5.1.2.3(强调我的)

lambda表达式的类型(也是闭包对象的类型)是一个唯一的,未命名的非并集类类型-称为闭包类型


实际上,您所做的是创建一个包含lambda副本的单例,每个lambda类型(per表示每个lambda表达式)都不同。这通常是有效的,因为lambda是不可变的,但是,可变lambda将揭示其区别:

auto f6 = [i = 1]( double, double ) mutable { return 6 * i++; };
int(*p6)(double,double) = get_ptr(f6);

assert( p6(0,0) == 6 );
assert( p6(0,0) == 12 );

int(*p6_prime)(double,double) = get_ptr(f6);
assert( p6_prime(0,0) == 18 ); // (!) p6_prime === p6
assert( p6(0,0) == 24 );

assert( f6(0,0) == 6 );
assert( f6(0,0) == 12 );
assert( f6(0,0) == 18 );
assert( f6(0,0) == 24 );

原因是您在最初的报告中的假设不正确:

但是,由于proxy是一个静态变量,因此每次调用函数时都会覆盖它

那不是真的。
static
变量在第一次调用函数时设置一次。对同一函数的进一步调用不会对语句求值,因此
proxy
将保留从第一次调用
get_ptr
的特定模板实例化时起的值

通常,您可以将
静态
声明和赋值分开,但在本例中,您不能这样做,因为lambda还没有默认构造函数。如果您这样做了,那么对lambda再次调用
get_ptr
将重置第一次调用的返回指针所使用的lambda,从而将上述结果更改为:

int(*p6_prime)(double,double) = get_ptr(f6);
assert( p6_prime(0,0) == 6 );
assert( p6(0,0) == 12 ); // (!) p6 === p_prime

f1
f5
无论如何不能是相同的类型,因为一个lambda返回1,另一个返回5。它们的
operator()
实现必然不同。(草案)规范说明如下(在5.1.2.3中):lambda表达式的类型(也是闭包对象的类型)是唯一的、未命名的非并集类类型-称为闭包类型…@Yuushi Perfect。请写一个答案!我喜欢这个关于编译器如何处理和处理lambda的一般化问题,以及注释,并且公认的答案可以作为回顾的参考。这是我的最爱@HenriMenke我认为它不会引发未定义的行为。这是被定义的行为,但(根据我的回答)可能是令人惊讶的行为。谢谢你的回答和评论。是的,事实上,我也注意到这个代码有各种各样的问题。然而,我的问题的核心,编译器是否为每个lambda生成不同的类型,得到了回答。我可能会在不久的将来编辑这个问题,所以没有人会想到实际使用这个代码。@HenriMenke告诉你一个秘密:我们到达这里时会做什么:我们读了你问题的标题(1行),我们立即跳转到答案“ok”。继续生活。不必费心编辑,只需保留原始内容以保持历史完整。@v.oddou我确实保留了原始内容。我只是在上面添加了一个免责声明。
int(*p6_prime)(double,double) = get_ptr(f6);
assert( p6_prime(0,0) == 6 );
assert( p6(0,0) == 12 ); // (!) p6 === p_prime