为啥在未计算的操作数中不允许使用 lambda 表达式,但在常量表达式的未计算部分中允许使用 lambda 表达式?

Posted

技术标签:

【中文标题】为啥在未计算的操作数中不允许使用 lambda 表达式,但在常量表达式的未计算部分中允许使用 lambda 表达式?【英文标题】:Why are lambda expressions not allowed in an unevaluated operands but allowed in the unevaluated portions of constant expressions?为什么在未计算的操作数中不允许使用 lambda 表达式,但在常量表达式的未计算部分中允许使用 lambda 表达式? 【发布时间】:2014-04-09 13:00:44 【问题描述】:

如果我们查看draft C++ standard 部分5.1.2 Lambda 表达式 段落2 说(强调我的前进): p>

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

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

条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式 (3.2),但是逻辑与 (5.14) 的子表达式,逻辑或 ( 5.15) 和未评估的条件 (5.16) 操作不被考虑 [...]

并有以下项目符号:

——一个 lambda 表达式 (5.1.2);

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

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

【问题讨论】:

【参考方案1】:

未评估的操作数排除的核心原因在C++ Standard Core Language Defect Reports and Accepted Issues #1607. Lambdas in template parameters 中进行了说明,它旨在澄清此限制并在5.1.2 部分中说明限制的意图是:

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

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

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

此提案已被接受,并在N3936(see this answer for a link)

为了更明确地讨论避免将 lambdas 作为 未计算的操作数的基本原理。 comp.lang.cpp.moderated Daniel Krü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 也成为一个问题,因为一旦您在函数签名中允许 lambdalambda 的主体也必须被破坏。这意味着要制定规则来破坏每一个可能的语句,这至少对某些实现来说是个负担。

【讨论】:

n3936 似乎已受密码保护(fdis?),链接也已损坏。 @dyp 嗯,今天早上它工作了,哦,好吧。 Loki 还链接到那个here,所以这可能是暂时的,让我看看我是否能弄清楚发生了什么。 (我认为它现在是 FDIS。open-std.org/jtc1/sc22/wg21/docs/papers/2014 上的新链接建议它:open-std.org/jtc1/sc22/wg21/prot/14882fdis/n3936.pdf) 另见D0315R2 看起来p0315r3 是accepted for C++20

以上是关于为啥在未计算的操作数中不允许使用 lambda 表达式,但在常量表达式的未计算部分中允许使用 lambda 表达式?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++11 中不允许重新定义 lambda,为啥?

为啥是 ”。” Access中的查询字段名称中允许但表字段名称中不允许?

在 QGraphicsScene 中拖动 QPixmap:如何避免 lambda 参数中不允许使用“自动”

为啥允许使用泛型 lambda 而不允许使用带有模板化方法的嵌套结构?

为啥存储函数中不允许使用动态 SQL?

为啥 TreeMap 中不允许空键?