【问题标题】:Yet another clang/gcc discrepancy regarding ODR usage?关于 ODR 使用的另一个 clang/gcc 差异?
【发布时间】:2016-07-04 11:23:29
【问题描述】:

为什么这段代码可以用 GCC(4.9 和 5+)编译,但不能用 clang(3.5-3.9)编译?

void test(const int&) { }
int main() {
  const int x = 42;
  auto f = []{ test(x); };
}

我有一些模糊的想法,认为差异与 ODR(一个定义规则)的使用有关,但我不太了解,无法弄清楚这里发生了什么。

【问题讨论】:

  • 有趣的是,如果你将 x 设为静态,clang 会很高兴
  • @RichardHodges 不需要捕获具有静态存储持续时间的变量。
  • 是否有人认为必须显式捕获上下文而不是编译器为我们找出它有点愚蠢?
  • @BitTickler 不,显式捕获是一件好事。它可以防止意外捕获循环引用(例如在捕获弱/共享 ptr 时)
  • @BitTickler 这就是捕获默认说明符 = 和 &

标签: c++ c++11 g++ clang++ one-definition-rule


【解决方案1】:

x 是 odr-used 因为它绑定到一个引用(test 的参数)。因此必须将其捕获 ([expr.prim.lambda]/13):

如果是 lambda 表达式 或函数调用的实例化 通用 lambda odr-uses ([basic.def.odr]) this 的运算符模板 或具有从其到达范围自动存储持续时间的变量, 该实体应被 lambda-expression 捕获。

违反此规则,就像标准中没有说“不需要诊断”或“未定义行为”的所有其他规则一样,require a diagnostic

不幸的是,GCC 在判断它是否是 odr-use 之前过早地执行常量折叠。这可能会导致问题such as [&]()->const int & { return x; } returning a dangling reference

【讨论】:

  • @M.M 你当然可以利用它来产生同样的效果,对吧?
  • @MM 我很确定 [class.local] /1 适用(因为 lambda 是本地类):本地类中的声明不应odr-use (3.2) 来自封闭范围的具有自动存储持续时间的变量。
  • @ecatmur 你有这方面的参考吗?在 N4140 中,术语“本地类”没有出现在 [expr.prim.lambda] 部分中。 (虽然这没有实际意义,因为 TC 发现有一个专门针对 lambdas 的等效规则)
  • @T.C.好的。我猜“一个实现可能以不同的方式定义闭包类型”不允许扩展到它在文件范围内声明闭包类型
【解决方案2】:

T.C.有正确的诊断,这里有一个更清晰的法律代码,其中 clang 做了正确的事情而 gcc 没有:

#include <iostream>

void test(const int&a) { std::cout << "in test() -- " << &a << "\n"; }
int main() {
  const int x = 42;
  std::cout << "in main() -- " << &x << "\n";
  auto f = [&]{ test(x); };
  f();
}

gcc 为引用捕获变量打印与原始地址不同的地址!

【讨论】:

  • 比这还要糟糕;尝试int main() { const int x = 42; return [&amp;]{ const int&amp; a = x; const int&amp; b = x; return &amp;a == &amp;b; }(); } - gcc 将两个引用绑定到不同的纯右值临时对象!
  • @ecatmur:抱歉打扰了。对于注释中的代码,真的很难看到 lambda 在哪里结束并且 main 再次继续。是的,那是邪恶的。
猜你喜欢
  • 2020-01-18
  • 2017-10-09
  • 2014-07-12
  • 2019-12-28
  • 2019-03-26
  • 2019-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多