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

时间:2018-05-29 13:29:20

标签: c++ language-lawyer one-definition-rule

我在here下面找到了示例。很明显,代码段中的注释是错误的,因为变量S::x被表达式&S::x使用了。{/ p>

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

请参阅live example

我理解编译器不需要发出这样的错误,因为[basic.def.odr]/10表示“不需要诊断”。但是为什么链接器不会发出关于未定义变量S::x的错误,就像在下面的代码中那样?

#include<iostream>
struct S { static const int x = 1; } s;

int main() {
    std::cout << &s.x << '\n';
}  

请参阅live example

3 个答案:

答案 0 :(得分:2)

  

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

因为它只是优化了!从不使用结果且没有副作用的表达式将被忽略。被忽略的东西不能被链接在一起。根本没有引用变量的代码,如果它的地址被采用但是没有被使用。

如您的wandbox示例所示,编译器会发出正确的诊断: &#34;表达结果未使用&#34;。

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

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

答案 1 :(得分:0)

你问:

  

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

虽然说:

  

[basic.def.odr]/10说&#34;无需诊断&#34;

您可以在&#34;无需诊断&#34; 中回答您自己的问题。不遵守odr规则是Undefined Behavior,链接器可能会引发错误或构建4D version of Tetris,但规格仍然可以!

并澄清&S::x使用或不使用x

  

除非应用左值到右值的转换,否则x的名称显示为可能评估的表达式ex的变量ex odr-used x产生一个常量表达式,它不会调用任何非平凡的函数,如果x是一个对象,ex是一个表达式e的潜在结果集的元素,其中lvalue-to-rvalue转换应用于e,或者e是丢弃值表达式。

对象的地址永远不是常量表达式。在S::x中使用了&S::x

证明最后一个断言:

  

[expr.const]/6

     

常量表达式是一个glvalue核心常量表达式,它指的是一个实体,它是一个常量表达式的允许结果(如下定义),或一个prvalue核心常量表达式,其值满足以下约束条件[...]

  

[expr.const]/2.7

     

2)表达式e核心常量表达式,除非根据抽象机器的规则评估e,评估以下表达式之一:
  [...]
  2.7)左值到左值的转换,除非它适用于

(以下所有要点均不适用:)

  

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

答案 2 :(得分:-1)

  

显然,代码段中的评论是错误的,因为变量 <p id="PID_value"></p> <script> function submitForm() { var x = document.getElementById("select_PM").value; document.getElementById("PID_value").innerHTML = "You selected: " + x; } window.addEventListener("load", submitForm); </script> S::x表达式使用了。

然后那个表达式被丢弃在位底上。 &S::x未使用ODR。一旦&S::x;第3段的变体读出(大胆强调我的):

  

名称显示为可能已评估的表达式[basic.def.odr]的变量xex 使用,除非申请lvalue-to-rvalue转换为ex会产生一个不调用任何非平凡函数的常量表达式,如果x是一个对象,x是该集合的一个元素。表达式ex的潜在结果,其中左值到右值转换应用于e,或e丢弃值表达式。< / p>

e段第11段涵盖了废弃价值表达的概念:

  

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

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

语句[expr]是一个丢弃值表达式,不需要左值到右值转换,因此无需转换。该声明可能也不存在,因此就ODR使用而言并不存在。

可以通过将&S::x;限定为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或更高版本编译/链接。关于C ++抽象机的工作原理,C ++ 11增加了很多。尽管--std=c++11仍然是一个废弃的值表达式,S::x;现在是volatile,但要求编译器在将结果丢弃到位底之前应用左值到右值的转换。

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