下面的程序在所有主要编译器上都可以成功编译:
“constexpr”变量初始化的规则是[dcl.constexpr]/10: (重点在于)
根据这些信息,我们可以得出结论:初始化的完整表达式
现在,[dcl.constexpr]/10告诉我们,完整表达式(即
针对上面的例子,我发现自己将[dcl.constexpr]/10解读为:“完整表达式
直观地说,我只是看到初始化声明符
struct S {
constexpr S(const S&){};
constexpr S() = default;
};
int main(void) {
S s1{};
constexpr S s2{ s1 };
}
“constexpr”变量初始化的规则是[dcl.constexpr]/10: (重点在于)
根据加粗部分,初始化的完整表达式应为常量表达式。 据我理解,这里的完整表达式是指[into.execution]/5中的init-declarator:在对象声明中使用“constexpr”修饰符将对象声明为const。这样的对象应具有字面类型并应进行初始化。 在任何“constexpr”变量声明中,初始化的完整表达式应为常量表达式(7.7)。 “constexpr”变量应具有恒定的销毁。
完整表达式是
- [..] (5.4) init-declarator ([dcl.decl]) [..]
init-declarator 的语法规则规定,init-declarator 是一个 declarator 后跟一个 initializer:
init-declarator:
declarator initializer
根据这些信息,我们可以得出结论:初始化的完整表达式
constexpr S s2{ s1 };
是s2{ s1 }
,其中s2
是一个声明符,{ s1 }
是一个初始化器。现在,[dcl.constexpr]/10告诉我们,完整表达式(即
s2{ s1 }
init-declarator)必须是常量表达式。我卡在这一点上了。根据[expr.const]/11,常量表达式是glvalue或prvalue核心常量表达式(带有一些附加限制)。针对上面的例子,我发现自己将[dcl.constexpr]/10解读为:“完整表达式
s2{s1}
应该是glvalue或prvalue核心常量表达式”。那么,初始化声明符s2{s1}
如何成为glvalue或prvalue核心常量表达式呢?正如您所看到的,我的解释导致了一个错误的理论。那么,我在这里错读/混淆了什么?直观地说,我只是看到初始化声明符
s2{s1}
是一个带有s1
作为参数的复制构造函数调用。但这不是我想要理解的问题。此外,为什么上面的程序是良好形式也不是我要问的。相反,我需要知道为什么我的解释没有得出合理的结论。
编辑
注意,我们在这个简单的例子(Demo)中也会遇到同样的问题:
int main(void) {
static const int i = 42;
constexpr int j = i;
constexpr const int &r = i;
}
constexpr int j = i;
的完整表达式是初始化声明符 j = i
,其中 j
是一个声明符,= i
是一个初始化器。那么,如何将完整表达式 j = i
作为 prvalue 核心常量表达式?
constexpr const int &r = i;
的完整表达式是初始化声明符 &r = i
,其中 &r
是一个声明符,= i
是一个初始化器。那么,如何将完整表达式 &r = i
作为 glvalue 核心常量表达式?