【问题标题】:Why is S::x not odr-used?为什么 S::x 不使用 ODR?
【发布时间】:2018-06-05 17:28:50
【问题描述】:

考虑这个来自cppreference的例子:

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

我同意 &S::x 是一个丢弃值表达式,因为标准说(9.2,第 1 段 [stmt.expr] from n4700

表达式语句的格式为

expression-statement:
    expression_opt ;

表达式是丢弃值表达式(第 8 条)...

但是,这足以让S::x 不被odr 使用吗? 6.2,第 3 段 [basic.def.odr] 状态

变量x 的名称显示为潜在评估表达式exex使用,除非

  • ...
  • 如果x是一个对象,ex是表达式e的潜在结果集合中的一个元素,其中任一
    • 左值到右值的转换 (7.1) 应用于 e,或
    • e 是弃值表达式(第 8 条)

问题是丢弃值表达式&S::x没有潜在结果(这意味着S::x不是&S::x的潜在结果),从6.2第2段[basic.def .odr]:

...表达式e的潜在结果集定义如下:

  • 如果 e 是 id 表达式 (8.1.4),则该集合仅包含 e
  • 如果e 是带有数组操作数的下标操作 (8.2.1),则该集合包含该操作数的潜在结果。
  • ...
  • 否则集合为空。

那么,你怎么解释S::x 不是 odr-used?

【问题讨论】:

  • 标准措辞的另一个问题是 S::x 不是 name 。这是一个合格的ID。但显然“一个变量 x 的名字......”这句话也应该适用于 x 是一个合格的 id。
  • 是的,这是我们这边的一个错误。固定。
  • @M.M 是这样的吗? github.com/cplusplus/draft/pull/1996
  • @T.C.制作废弃值表达式&S::x odr-use x 的合理性是什么?
  • @xskxzr 获取某物的地址通常需要所说的东西存在。为这种情况划出一个例外有什么好处?

标签: c++ language-lawyer c++17


【解决方案1】:

它确实是 odr-used。你的分析是正确的(我修复了那个例子a while ago)。

【讨论】:

  • 我不打算写一个答案只是为了确认我在 cmets 中写的内容,但由于某种原因,这个问题吸引了很多错误的答案:|
  • 我很感激。谢谢!
【解决方案2】:

是的,在示例中,&S::x odr-uses S::x

[basic.def.odr]/4

一个变量x,其名称显示为一个潜在的评估表达式ex,被exodr-used,除非将左值到右值转换应用到x产生一个不调用任何重要函数的常量表达式,如果 x 是一个对象,ex 是表达式 e 的潜在结果集合中的一个元素,其中左值到右值的转换应用于 e,或者 e 是丢弃值表达式。

对象的地址永远不是常量表达式。这就是为什么 S::x&S::x 中使用 ODR。

证明最后一个断言是正确的:

[expr.const]/6

常量表达式是指代一个作为常量表达式(如下定义)的允许结果的实体的泛左值核心常量表达式,或者是其值满足以下约束的纯右值核心常量表达式 [...]

[expr.const]/2.7

2) 表达式 e 是一个核心常量表达式,除非 e 的评估,遵循抽象机器的规则,将评估其中一个以下表达式:
[...]
2.7) 左值到右值的转换,除非它应用于

(以下都不适用:)

2.7.1) 一个整数或枚举类型的非易失性左值,它引用一个完整的非易失性 const 对象,该对象具有前面的初始化,用常量表达式初始化,或
2.7.2) 引用字符串字面量的子对象的非易失性左值,或
2.7.3) 一个非易失性左值,它引用一个用 constexpr 定义的非易失性对象,或引用此类对象的一个​​非可变子对象,或
2.7.4) 文字类型的非易失性左值,它引用一个非易失性对象,该对象的生命周期在 e 的评估中开始;

【讨论】:

  • &S::x 不是左值(因此它不能进行左值到右值的转换),并且& 不会导致S::x 进行左值到右值的转换,那么你为什么要引用左值到右值转换的规则呢?
【解决方案3】:

当声明const int 时,它可能会被编译器完全丢弃,除非你使用它的地址。 只取地址是不够的。

Discarded 并不意味着该值不被评估,它是,但它意味着没有包含 const 值的内存地址,编译器只是将 const 变量替换为其值,因为它只是一个宏。

另外,当取一个指向它的指针并从指针中取回值时,编译器并没有太多印象,它只是忽略它并使用该值。

下面的代码显示了它,这个代码可以编译和运行(我通过几个编译器测试它,我仍然不确定它是否被所有人成功编译......)尽管S::x不是声明:

#include <iostream>
using namespace std;
struct S
{
    static const int x=0; 
};
//const int S::x; //discarded
int main()
{
    const int *px = &S::x;  //taking the address
    cout<< *px <<endl; //print the value - OK
    return 0;
}

但如果我尝试使用地址本身(而不是值),例如:

cout<< px <<endl; //print the address - Will be failed

链接将失败到:“未定义对S::x的引用”。

因此,我的结论是:获取地址而不使用它,根本不算数

【讨论】:

  • 问题被标记为language-lawyer,这意味着 OP 希望得到引用 ISO C++ 标准的答案。它编译是无关紧要的(实际上很多 ODR 违规编译都很好)。
猜你喜欢
  • 2013-11-06
  • 1970-01-01
  • 2014-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-04
  • 2010-09-11
  • 1970-01-01
相关资源
最近更新 更多