为什么这段代码可以用 GCC(4.9 和 5+)编译,但不能用 clang(3.5-3.9)编译?
void test(const int&) { }
int main() {
const int x = 42;
auto f = []{ test(x); };
}
我有一些模糊的想法,即差异与 ODR(一个定义规则)的使用有关,但我不太了解,无法弄清楚这里发生了什么。
为什么这段代码可以用 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
是 odr-used 因为它绑定到一个引用(test
的参数)。因此必须捕获它([expr.prim.lambda]/13):
如果lambda 表达式或通用 lambda odr-uses ([basic.def.odr]) 的函数调用运算符模板的实例化
this
或具有从其到达范围内自动存储持续时间的变量,则该实体应由lambda捕获-表达式。
违反此规则,就像标准中没有说“不需要诊断”或“未定义行为”的所有其他规则一样,需要诊断。
不幸的是,GCC 在判断它是否是 odr-use 之前过早地执行常量折叠。这可能会导致诸如[&]()->const int & { return x; }
返回悬空引用等问题。
TC 有正确的诊断,这里有一个更清晰的法律代码,其中 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 为引用捕获变量打印与原始地址不同的地址!