说编译器可以用它的值 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 替换。说这是有效的是否正确,因为 a
是 not 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
不会产生常量表达式。
您的分析在另外两个方面被破坏:
- "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
$ _
下面的代码在 GCC、clang 和 VS2017 中编译,return
语句中的表达式 a->i
被其常量值 1 替换。说这是有效的是否正确,因为 a
是 not 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 expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion (7.1) tox
yields a constant expression (8.6) that does not invoke any non-trivial functions and, ifx
is an object,ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion (7.1) is applied toe
, ore
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
不会产生常量表达式。
您的分析在另外两个方面被破坏:
- "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 ofe
, 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
$ _