如果我们查看draft C++ standard部分5.1.2
Lambda表达式段 2 说(强调我的前进 ):
lambda表达式的评估导致prvalue临时(12.2)。这个临时对象称为闭包对象。 lambda表达式不应出现在未评估的操作数中(第5条)。 [注意:闭包对象的行为类似于函数对象(20.8).- end note]
和部分5.19
常量表达式段 2 说:
条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2),但是逻辑AND(5.14)的子表达式,逻辑OR( 5.15)和未评估的条件(5.16)操作不被视为 [...]
并有以下子弹:
- lambda表达式(5.1.2);
那么为什么lambda表达式不允许在未评估的操作数中,但是在常量表达式的未评估部分中是允许的?
我可以看到未评估的操作数如何在几种情况下( decltype或typeid )的类型信息不是很有用,因为每个lambda都有一个唯一的类型。虽然为什么我们想要在未经评估的不断表达的语境中允许它们,但也许不清楚,或许允许SFINAE?
答案 0 :(得分:17)
未评估的操作数排除的核心原因在C++ Standard Core Language Defect Reports and Accepted Issues #1607. Lambdas in template parameters中有所涉及,旨在澄清这一限制,并说明5.1.2
部分中限制的意图是:< / p>
[...]避免在功能模板签名[...]
中处理它们的需要
正如问题文件所述,当前的措辞实际上有一个漏洞,因为常量表达式允许它们在未评估的上下文中。但它没有直截了当地说明这种限制的理由。避免名称修改的愿望脱颖而出,你可以推断,避免扩展 SFINAE 也是理想的,因为提议的解决方案试图收紧限制,即使有几个可行的替代方案允许 SFINAE 。 5.1.2
段 2 的修改版本如下:
lambda表达式不应出现在未评估的操作数(第5条[expr]),中的模板参数,别名声明,typedef声明或函数声明中。函数体外的函数模板和默认参数[注意:目的是防止lambda出现在签名中 - 注释] 。 [注意:闭包对象的行为类似于函数对象(20.10 [function.objects])。 - 后注]
此提案已被接受并位于N3936
( see this answer for a link )
有关避免将lambda作为未评估操作数的基本原理的更明确的讨论。 comp.lang.cpp.moderated DanielKrügler上题为Rationale for lambda-expressions not being allowed in unevaluated contexts的讨论列出了三个原因:
可能SFINAE个案例的极端扩展:
[...]他们被排除在外的原因是由于sfinae案件的这种极端扩展(你为编译器打开了一个潘多拉盒子)[...]
在很多情况下它只是无用,因为每个lambda都有一个独特的类型,假设的例子给出:
template<typename T, typename U>
void g(T, U, decltype([](T x, T y) { return x + y; }) func);
g(1, 2, [](int x, int y) { return x + y; });
声明和调用中lambda的类型不同(按定义),因此无法正常工作。
Name mangling也成为问题,因为一旦你在函数签名中允许 lambda , lambda 的主体就必须被修改为好。这意味着要制定规则以破坏每个可能的语句,这对于至少某些实现来说会很麻烦。