想一想constexpr
是什么意思。它的意思是我可以在编译时解析这个值。
因此,类的成员变量本身不能是constexpr
...xVal
所属的实例直到实例化时才存在!拥有xVal
的东西可能是constexpr
,这将使xVal
成为constexpr
,但xVal
本身永远不可能是constexpr
。
这并不意味着这些值不能是常量表达式...实际上,类的constexpr
实例可以使用这些变量作为常量表达式:
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
int xVal { 0 };
int yVal { 0 };
};
constexpr S s;
template <int f>
int foo() {return f;}
int main()
{
cout << "Hello World" << foo<s.xVal>( )<< endl;
return 0;
}
编辑:下面有很多关于本文中存在一些隐含问题的讨论。
"为什么不能通过声明其成员为constexpr,强制所有类的实例都是constexpr的?"
看下面的例子:
struct S;
struct A {std::unique_ptr<S> x; void Foo(); A();}
int main(int argc, char** argv) {
A a;
a->foo();
}
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
constexpr int xVal { 0 };
constexpr int yVal { 0 };
};
A和S的定义可能在完全不同的编译单元中,因此如果忘记了A的实现,那么S必须是constexpr的事实直到链接时才能知道。这种模棱两可的情况很难调试和实现。更糟糕的是,S的接口可能完全暴露在共享库、COM接口等中...这可能会完全改变共享库的所有基础设施,这可能是不可接受的。
另一个原因是这种影响力有多具有传染性。如果类的任何成员都是constexpr的,则所有成员(以及它们的所有成员)和所有实例都必须是constexpr的。看下面的场景:
struct S {
constexpr S(int x, int y): xVal(x), yVal(y) {}
constexpr S(int x): xVal(x) {}
constexpr S() {}
constexpr int xVal { 0 };
int yVal { 0 };
};
任何S的实例都必须是constexpr才能保持一个排他性constexpr的xval。因为xVal是constexpr,所以yVal本质上也变成了constexpr。在技术上没有编译器原因阻止你这样做(我不认为有),但它不感觉很像C++。
“好吧,但我真的想使类的所有实例都是constexpr。是什么技术限制阻止我这样做?”
可能除了标准委员会认为这不是个好主意外,没有其他任何限制。就个人而言,我认为这没有多少用处……我不想限制人们如何使用我的类,只是定义当他们使用它时我的类应该如何行事。当他们使用它时,他们可以将特定实例声明为constexpr(如上所述)。如果我有一些需要使用constexpr实例的代码块,我会使用模板来实现:
template <S s>
function int bar(){return s.xVal;}
int main()
{
cout << "Hello World" << foo<bar<s>()>( )<< endl;
return 0;
}
我认为您最好使用一个constexpr函数,这样它就可以在严格和非严格的方式下都可以使用。
constexpr int bar(S s) { return s.xVal; }