在未评估的上下文中(例如在decltype中)不允许使用Lambda表达式,并且直到最近才能使用常量表达式。因此,无法在模板参数中使用它们。
在C ++ 17中,常量表达式lambdas是可能的。这仍然不允许在模板参数中使用它们。
但是特别是对于非类型模板参数,常量表达式lambda表达式可以在计算上下文中使用,例如:
template<int N> struct S { constexpr static int value = N; };
int main() {
int N = S<[]()constexpr{return 42;}()>::value;
}
但仍然无效,因为在模板参数中明确禁止使用lambda表达式,无论是类型还是非类型。
我的问题是不允许上述构造背后的原因。我可以理解函数签名中的lambda类型可能有问题,但这里闭包类型本身是无关紧要的,只使用了(编译时常量)返回值。
我怀疑原因是lambda体中的所有语句都将成为模板参数表达式的一部分,因此如果体内的任何语句在替换期间格式不正确,则需要应用SFINAE。可能需要编译器开发人员的大量工作。
但这实际上是我的动力。如果可以使用上面的构造,那么SFINAE不仅可以用于常量表达式,还可以用于constexpr函数中有效的其他语句(例如文字类型声明)。
除了对编译器编写者的影响之外,这是否会导致任何问题,例如:标准中含糊不清,矛盾或并发症?
答案 0 :(得分:2)
非常有意的是,lambdas不会出现在未评估的情境中。 lambdas总是具有独特类型的事实导致各种各样的问题。
以下是来自Daniel Krugler的comp.lang.c++ discussion的几个例子:
确实存在大量允许lambda的用例 表达式,它可能会极大地扩展可能的sfinae案例 (包括完整代码“sand-boxes”)。他们成为的原因 排除是由于sfinae案件的这种极端扩展(你 正在为编译器打开一个Pandora盒子,以及它可以的事实 导致你的其他例子出现问题,例如
template<typename T, typename U> void g(T, U, decltype([](T x, T y) { return x + y; }) func);
没用,因为每个lambda表达式都会生成一个唯一的类型,所以
之类的东西g(1, 2, [](int x, int y) { return x + y; });
实际上不起作用,因为lambda中使用的lambda的类型 参数不同于
g
调用中lambda的类型。最后它确实导致了名称错位问题。例如。当你有
template<typename T> void f(T, A<sizeof([](T x, T y) { return x + y; })> * = 0);
在一个翻译单元中,但
template<typename T> void f(T, A<sizeof([](T x, T y) { return x - y; })> * = 0);
在另一个翻译单元。现在假设您实例化
f<int>
来自两个翻译单位。这两个功能有所不同 签名,因此他们必须生成不同的模板 实例。保持它们分开的唯一方法是破坏它 lambda的身体。反过来,这意味着编译器编写者具有 为每一种陈述提出名称修改规则 语言。虽然技术上可行,但这被认为是一个 规范和实施负担。
这是一大堆问题。特别是考虑到你的写作动机:
int N = S<[]()constexpr{return 42;}()>::value;
可以通过编写来轻松解决:
constexpr auto f = []() constexpr { return 42; }
int N = S<f()>::value;