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

Is it correct to say that the compiler can replace the expression `a->i` below by its value 1 because...?

下面的代码在 GCC、clang 和 VS2017 中编译,return 语句中的表达式 a->i 被其常量值 1 替换。说这是有效的是否正确,因为 anot odr-used in the expression a->i?.

struct A 
{ 
    static const int i = 1; 
}; 
int f() 
{ 
    A *a = nullptr; 
    return a->i;
}

PS:我相信 a 而不是 odr-用于表达式 a->i 因为它满足 "unless" [basic.def.odr]/4中的条件如下:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (7.1) to x yields a constant expression (8.6) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (7.1) is applied to e, or e is a discarded-value expression (8.2).

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

a 是 odr-used 因为你没有通过 "unless":

的第一部分

applying the lvalue-to-rvalue conversion (7.1) to x yields a constant expression (8.6) that does not invoke any non-trivial functions

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

剩下的就是核心问题315 and 232


您的分析在另外两个方面被破坏:

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

[expr.const]/2.7:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

  • [...]
  • an lvalue-to-rvalue conversion unless it is applied to
    • a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
    • a non-volatile glvalue that refers to a subobject of a string literal, or
    • a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
    • a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;
  • [...]

i 是 class 的 static 成员...因为您可以通过使用常规访问 class 的 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
$ _