为什么链接器在下面的代码中不发出错误?



我在这里找到了下面的示例。显然,代码段中的注释是错误的,因为变量S::x由表达式&S::x使用 odr。

struct S { static const int x = 1; };
void f() { &S::x; } // discarded-value expression does not odr-use S::x
int main(){
f();
}

查看实时示例

我知道编译器不需要发出这样的错误,因为 [basic.def.odr]/10 说"不需要诊断"。但是为什么链接器没有像下面的代码中那样发出有关未定义变量S::x的错误?

#include<iostream>
struct S { static const int x = 1; } s;
int main() {
std::cout << &s.x << 'n';
}  

请参阅实时示例。

但是为什么链接器没有像下面的代码那样发出有关未定义的变量 S::x 的错误?

因为它只是优化出来的!结果从不使用且没有副作用的表达式将被简单地忽略。被忽略的内容绝不能链接。根本没有引用变量的代码,如果它的地址被获取但随后未被使用。

如 wandbox 示例中所示,编译器会发出正确的诊断: "表达式结果未使用"。

未使用的代码以后不会导致链接器错误;)

您的第二个示例使用值(var 的地址),因此需要计算表达式。这会向链接器发出代码,其中地址的符号在任何地方都找不到。

你问:

[为什么] 链接器不发出错误?

一边说:

[basic.def.odr]/10 说"无需诊断">

您在"无需诊断">中回答自己的问题。不遵守 odr 规则是未定义的行为,链接器可能会引发错误或构建俄罗斯方块的 4D 版本,但按照规范

仍然可以!

并澄清&S::x是否使用odrx

名称显示为潜在计算表达式的变量xexex使用 odr,除非对x应用左值到右值的转换会产生不调用任何非平凡函数的常量表达式,并且如果 x 是一个对象,则 ex 是表达式 e 的潜在结果集合的元素, 其中,左值到右值的转换应用于 e,或者 e 是丢弃值表达式。

对象的地址从来都不是常量表达式。S::x&S::x中使用

。为了证明最后一个断言的合理性:

[expr.const]/6

常量表达式要么是 glvalue 核心常量表达式,它引用的实体是常量表达式(定义如下)的允许结果,要么是 prvalue 核心常量表达式,其值满足以下约束 [...]

[expr.const]/2.7

2)表达式e核心常量表达式,除非按照抽象机器的规则计算e将计算以下表达式之一:
[...]
2.7) 左值到右值的转换,除非它适用于

(以下任何要点均不适用:)

2.7.1) 整数或枚举类型的非易失性 gl值,指的是具有前面初始化的完整非易失性 const 对象,使用常量表达式初始化,或
2.7.2) 引用字符串文本的子对象的非易失性 glvalue 或
2.7.3) 非易失性 gl值,指使用 constexpr 定义的非易失性对象,或指此类对象的不可变子对象,或
2.7.4) 文字类型的非易失性 gl值,指的是其生命周期始于e评估的非易失性对象;

显然,代码段中的注释是错误的,因为变量S::x被表达式&S::x使用 odr。

然后这个表达式被扔在位地板上。&S::x;未使用 ODR。第3段[basic.def.odr]变文如下(粗体强调我的):

一个变量x,其名称显示为潜在计算表达式exex使用 odr,除非x应用 lvalue-rvalue 转换会产生一个不调用任何非平凡函数的常量表达式,并且如果x是一个对象,则ex是表达式e的潜在结果集的一个元素, 其中左值到重值的转换应用于e,或者e丢弃值表达式

第 11 段[expr]部分介绍了丢弃值表达式的概念:

在某些情况下,表达式仅针对其副作用而出现。此类表达式称为丢弃值表达式。计算表达式并丢弃其值。不应用数组到指针和函数到指针的标准转换。当且仅当表达式是易失限定类型的 glvalue 并且它是以下值之一时,才应用左值到右值转换:

  • (表达式),其中表达式是这些表达式之一,
  • 身份表达式,
  • 下标,
  • 类成员访问,
  • 间接
  • 指向成员的指针操作,
  • 条件表达式,
  • 其中第二个和第三个操作数都是这些表达式之一,或者
  • 逗号表达式,其中右操作数是这些表达式之一。

语句&S::x;是一个丢弃值表达式,不需要左值到右值的转换,因此没有要转换的内容。该语句也可能不存在,因此就 ODR 使用

而言不存在。可以通过将S::x限定为volatile而不是const来更改此指标,并尝试将S::x放在位底:

struct S { static volatile int x; };
void f() { S::x; } // This discarded-value expression does odr-use S::x
int main(){
f();
}

上述编译(但带有关于丢弃表达式的警告)使用 GNU 的 C++ 编译器/链接器使用--std=c++03但无法使用--std=c++11或更高版本进行编译/链接。C++11 增加了相当多关于C++抽象机器的工作原理。尽管S::x;仍然是丢弃的值表达式,但该S::x现在易失性,要求编译器在将结果放在位底之前应用左值到右值的转换。

将上面的S::x更改为&S::x,程序将再次编译(但再次发出有关丢弃表达式的警告)。即使S::x是易失性的,它的地址也不是。

相关内容

  • 没有找到相关文章

最新更新