下面的代码在GCC,clang和VS2017中编译,a->i
语句中的表达式return
被其常量值1替换。说这是有效的,因为{{1在表达式a
?
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
,其名称显示为可能已评估的名称 表达式x
由ex
使用,除非应用 左值到右值的转换(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
。
答案 0 :(得分:12)
a
因使用了#34的第一部分失败而被使用,除非":
将左值到右值转换(7.1)应用于
x
会产生一个不调用任何非平凡函数的常量表达式(8.6)
将左值到右值的转换应用于a
不会产生常量表达式。
您的分析还有两种方式:
.
form of class member access定义,因此在应用[basic.def.odr] /2.3之前,需要将a->i
重写为点形式,即(*a).i
。 a
不是该表达式的潜在结果集的成员。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
$ _