20

如果我们查看C++ 标准部分草案5.1.2 Lambda 表达式2段说(强调我的未来):

对 lambda 表达式的求值会产生一个临时纯右值 (12.2)。这个临时对象称为闭包对象。lambda 表达式不应出现在未计算的操作数中(第 5 条)。[注意:闭包对象的行为类似于函数对象(20.8)。-结束注释]

和部分5.19 常量表达式2段说:

条件表达式是核心常量表达式,除非它涉及以下之一作为可能计算的子表达式 (3.2),但逻辑与 (5.14)、逻辑或 (5.15) 和条件 (5.16) 操作的子表达式未计算不被视为[...]

并具有以下项目符号:

— 一个 lambda 表达式 (5.1.2);

那么为什么 lambdas 表达式不允许在未计算的操作数中使用,但在常量表达式的未计算部分中却允许呢?

我可以看到对于未评估的操作数,在几种情况下(decltypetypeid)的类型信息不是很有用,因为每个 lambda 都有一个唯一的类型。虽然我们为什么要在常量表达式的未评估上下文中允许它们尚不清楚,但也许是为了允许SFINAE

4

1 回答 1

23

C++ Standard Core Language Defect Reports and Accepted Issues #1607中介绍了未评估操作数排除的核心原因。模板参数中的 Lambda 旨在澄清此限制并在部分中说明限制的意图5.1.2是:

[...] 无需在函数模板签名中处理它们 [...]

作为问题文档,当前的措辞实际上有一个漏洞,因为常量表达式允许它们在未评估的上下文中。但它并没有直接说明这种限制的理由。避免名称修改的愿望很突出,您可以推断,避免扩展SFINAE也是需要的,因为提议的决议试图加强限制,即使有几个可行的替代方案允许SFINAE5.1.22段的修改版本如下:

lambda 表达式不得出现在未计算的操作数(第 5 条 [expr])、模板参数、别名声明、typedef 声明或函数体之外的函数或函数模板的声明中和默认参数 [注意:目的是防止 lambdas 出现在签名中——结束注释]。[注意:闭包对象的行为类似于函数对象(20.10 [function.objects])。——尾注]

该提案已被接受并在N3936请参阅此答案以获取链接

更明确地讨论避免将 lambdas 作为未计算的操作数的基本原理。comp.lang.cpp.moderated Daniel Krügler上题为lambda-expressions not allowed in unevaluate contexts的讨论提出了三个原因:

  1. 可能的SFINAE案例的极端扩展:

[...]它们被排除在外的原因正是由于 sfinae 案例的这种极端扩展(您正在为编译器打开一个潘多拉盒子)[...]

  1. 在许多情况下,它只是无用的,因为每个 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 类型不同(根据定义),因此这是行不通的。

  2. 名称修改也成为一个问题,因为一旦您在函数签名中允许使用lambda ,则lambda的主体也必须被修改。这意味着要制定规则来破坏每一个可能的语句,这对于至少某些实现来说是很麻烦的。

于 2014-03-06T17:49:39.177 回答