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

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

本文关键字:替换 是否 因为 gt 编译器 表达式      更新时间:2023-10-16

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

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

PS:我相信a在表达式中没有使用a->i,因为它满足 [basic.def.odr]/4 中的"除非"条件,如下所示:

名称显示为潜在计算值的变量x表达式exex使用 ODR,除非应用 左值到重值的转换 (7.1( 到x产生常量表达式 (8.6( 不援引任何非平凡的内容 函数,如果x是一个对象,则ex是表达式e的潜在结果集合的一个元素,其中 左值到重值转换 (7.1( 应用于e,或者e丢弃值表达式 (8.2(。

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

a被 ODR 使用,因为你失败了"除非"的第一部分:

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

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

其余的是核心问题315和232。


您的分析以另外两种方式中断:

  • "对象表达式"是使用类成员访问的.形式定义的,因此在应用 [basic.def.odr]/2.3 之前,您需要将a->i重写为点形式,即(*a).ia不是该表达式的潜在结果集的成员。
  • 该项目符号本身是有缺陷的,因为它是在考虑非静态数据成员的情况下编写的。对于静态数据成员,潜在结果集实际上应该是命名的静态数据成员 - 请参阅核心问题 2353,因此a加倍不是该表达式的潜在结果集的成员。

[expr.const]/2.7:

表达式e核心常量表达式,除非e的评估,遵循抽象机器的规则,将 计算以下表达式之一:

  • [...]
  • 左值到右值的转换,除非它应用于
    • 整数或枚举类型的非易失性 gl值,引用具有前面的完整非易失性 const 对象 初始化,使用常量表达式初始化,或
    • 引用字符串文本的子对象的非易失性 glvalue ,或
    • 一种非易失性
    • gl值,它引用用constexpr定义的非易失性对象,或者引用 这样的对象,或
    • 文字类型的非易失性 gl值,指的是其生命周期始于e评估的非易失性对象;
  • [...]

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
$ _