为什么可以通过对象 's' 直接使用方法 'func()' 来初始化 constexpr 变量('x' 或 'y'),而不是通过指针变量 'p'(对于 'z') ?此外,对象“s”本身以及相应的指针“p”都不是常量表达式。
struct S {
auto constexpr static size() { return 42; }
auto constexpr func() const { return 42; }
};
int main() {
S s;
auto p{&s};
auto constexpr x{s.func()}; // OK
auto constexpr y{(&s)->func()}; // OK
auto constexpr z{p->func()}; // ERROR
return 0;
}
我试图根据以下问题来解决这个问题 [1][2],但他们只解决了“constexpr 函数在编译时和运行时可用”。 1 2
这是关于“动态类型为 constexpr-unknown 的对象”,这是P2280R4引入的概念。该论文有一整节关于允许使用
(&s)->func()
但不允许使用 p->func()
的原因,标题为 “nullptr
怎么样?”.
从机械上讲,这是通过标准中的这一行实现的([expr.const]p9):
在将表达式 E 作为核心常量表达式求值期间,所有引用其生命周期不以 E 求值开始的对象或引用的
id-expressions和*this
的使用均被视为指的是该对象或引用的特定实例,其生命周期和所有子对象(包括所有联合成员)的生命周期包括整个常量评估。
因此
s
和 p
都具有在不断求值期间 constexpr 未知的动态类型,因此您可以使用它们,但无法访问它们的值。
这意味着
s.func()
没问题,因为 func
无法访问该对象。
p->func()
不是,因为您试图通过隐式取消引用来访问指针。
(&s)
can 在持续评估过程中被取消引用。 &s
不像 p
那样是 constexpr-unknown,并且它的值可以在持续评估期间使用(它只是 s
),所以 (&s)->func()
很好。