【问题标题】:lambdas in unevaluated contexts (Until C++20)未计算上下文中的 lambdas (C++20 前)
【发布时间】:2019-09-07 09:27:34
【问题描述】:

我目前正在阅读 P0315R1 论文,该论文讨论了未评估上下文中的 Lambdas

文档中有一个声明解释了为什么 lambdas 不能出现在未评估的上下文中(当然只有在 C++20 之前),如下所示:

Lambda 是一种非常强大的语言功能,尤其是当它出现时 使用带有自定义谓词或表达的高阶算法 小的,一次性的代码。然而,他们遭受了一个重要的 限制,削弱了它们对创造性用例的有用性; 它们不能出现在未评估的上下文中。 此限制是 最初旨在防止 lambda 出现在签名中, 这会打开一罐蠕虫进行破坏,因为 lambdas 是 需要有唯一的类型。

有人可以用一个例子来解释这个说法吗?

【问题讨论】:

  • @VTT 谢谢,我会看看链接。
  • 了解名称修饰的含义,并考虑如何将其应用于出现在签名中的 lambda。
  • @PasserBy 感谢您的评论。我已经知道 mangling 是什么名字。但是,我必须了解它如何应用于 lambda。谢谢你的好建议。

标签: c++ c++11 lambda c++20


【解决方案1】:

一点背景知识:链接器不理解函数重载——他们只理解 C 语言中的函数名称。这就是 C++ 编译器会破坏您的函数名称的原因。 void foo(int) 变为 _Z3fooi。损坏的名称对所有参数的类型进行编码。如果您的函数是由模板实例化产生的,那么所有模板参数也会被编码。如果它们本身是模板类,则它们的模板参数会被编码,以此类推,直到达到 int 或函数指针等原始类型。

Lambda 使这变得具有挑战性,因为每个 lambda 都需要具有不同的类型。这对于函数中定义的 lambdas 来说还不错:

auto foo() { return [](){}; }
auto bar() { return [](){}; }

foobar 返回不同的类型。如果您随后将它们传递给另一个函数模板,则该模板的名称会被破坏,就像 foo::__lambda1 或类似的东西一样。

让 lambda 出现在 decltype 中会破坏这种机制。

void bar(decltype([](){}));
void bar(decltype([](){})) {}

这是原型和定义吗?还是这两个不同的bar 重载?如何在翻译单元中识别它们(如何破坏名称)?

直到现在,C++ 甚至都禁止问这个问题。您链接的论文给出了答案:这样的事情不能有联系。甚至不要试图破坏它们。

【讨论】:

  • 您的bars 需要在一个类中,以便 ODR 将它们统一起来(否则它们可能只是通过内部链接发出)。
  • 这是真的吗?它们不是静态的,也不在匿名命名空间中。
  • 在真正的工具(而不是抽象语言定义)级别上是正确的:注意.globl directives
  • @Filipp 我想我必须阅读更多关于此的内容。你能指点我有关这方面的一些文档或参考资料的方向吗?
  • 我不能,抱歉。尝试在 Godbolt 编译器资源管理器上使用 lambdas 和模板。查看完整的符号名称。关闭拆解。故意犯错,看看编译器会打印什么。
【解决方案2】:

如果我们在头文件的函数签名中有一个 decltype of lambda 的内联函数。编译器会在包含该函数的每个翻译单元中为该函数生成一个唯一的错位名称。

所以最后链接器无法合并“相同”函数的这些多个定义,因为损坏的名称不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 2020-05-24
    相关资源
    最近更新 更多