说编译器可以将下面的表达式'a-> i`替换为值1是正确的,因为......?

时间:2018-05-19 18:31:10

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

下面的代码在GCC,clang和VS2017中编译,a->i语句中的表达式return被其常量值1替换。说这是有效的,因为{{1在表达式a

中使用了 odr
a->i

PS:我相信struct A { static const int i = 1; }; int f() { A *a = nullptr; return a->i; } 在表达式a中使用了而不是,因为它满足"除非" [basic.def.odr]/4中的条件如下:

  

变量a->i,其名称显示为可能已评估的名称   表达式xex 使用,除非应用   左值到右值的转换(7.1)到ex产生一个常量表达式   (8.6)不引用任何非平凡的       函数,如果x是一个对象,x是表达式ex的潜在结果集的一个元素,其中   左值到右值转换(7.1)适用于e,或e是   丢弃值表达式(8.2)。

特别是,表达式e是表达式ex == a的潜在结果集合的元素,根据[basic.def.odr]/2 (2.3),包含表达式e == a->i,其中左值到右值的转换适用于ex

2 个答案:

答案 0 :(得分:12)

a因使用了#34的第一部分失败而被使用,除非":

  

将左值到右值转换(7.1)应用于x会产生一个不调用任何非平凡函数的常量表达式(8.6)

将左值到右值的转换应用于a不会产生常量表达式。

其余的是核心问题315232

您的分析还有两种方式:

  • "对象表达式"使用. form of class member access定义,因此在应用[basic.def.odr] /2.3之前,需要将a->i重写为点形式,即(*a).ia不是该表达式的潜在结果集的成员。
  • 该子弹本身存在缺陷,因为它是在考虑非静态数据成员的情况下编写的。对于静态数据成员,潜在结果集实际上应该是命名的静态数据成员 - 请参阅core issue 2353,因此a双倍不是该表达式的潜在结果集的成员。

[expr.const] /2.7:

  

表达式e核心常量表达式,除非   根据抽象机器的规则评估e   评估以下表达式之一:

     
      
  • [...]
  •   
  • 一个左值到右值的转换,除非它适用于      
        
    • 一个非整数或枚举类型的非易失性glvalue,它引用一个完整的非易失性const对象   初始化,用常量表达式初始化,或
    •   
    • 一个非易失性glvalue,它引用字符串文字的子对象,或
    •   
    • 一个非易失性glvalue,它引用一个用constexpr定义的非易失性对象,或者引用一个不可变的子对象   这样的对象,或
    •   
    • 文字类型的非易失性glvalue,引用一个非易失性对象,其生命周期始于e的评估;
    •   
  •   
  • [...]
  •   

答案 1 :(得分:-2)

i是该类的static成员...因为您可以通过使用实例的常规方法访问该类的static成员,它们不依赖于特别是任何实例,因此您不需要取消引用nullptr指针(就像使用sizeof运算符时一样)。您也可以使用简单的

访问该字段
return A::i;

语句,因为您不需要创建实例来访问它。实际上,作为const,编译器允许将其作为常量值进行管理,因此只有在您需要使用它的地址(通过&运算符)的情况下,编译器才能绕过分配它只读存储器。

以下示例将探测:

#include <iostream>

struct A { 
    static const int i = 1; 
}; 

int main()
{
    std::cout << ((A*)0)->i << std::endl;
    std::cout << A::i << std::endl;
}

将打印

$ a.out
1
1
$ _