为什么非静态数据成员不能用constexpr?

问题描述 投票:0回答:1

这是有效代码:

struct S {
  constexpr S(int x, int y): xVal(x), yVal(y) {}
  constexpr S(int x): xVal(x) {}
  constexpr S() {}

  const int xVal { 0 };
  const int yVal { 0 };
};

但在这里我真的想声明

xVal
yVal
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 };         // error!
  constexpr int yVal { 0 };         // error!
};

如前所述,代码无法编译。原因是(根据 7.1.5/1),只能声明静态数据成员

constexpr
。但是为什么

c++ c++11 constexpr
1个回答
59
投票

想想

constexpr
是什么意思。这意味着我可以在编译时解析这个值。

因此,类的成员变量本身不能是

constexpr
...
xVal
所属的实例在实例化时才存在!拥有
xVal
的东西可能是
constexpr
,这将使
xVal
成为
constexpr
,但
xVal
永远不可能单独成为
constexpr

这并不意味着这些值不能是 const 表达式...事实上,该类的 constexpr 实例可以将变量用作 const 表达式:

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>//requires a constexpr
int foo() {return f;}

int main()
{
   cout << "Hello World" << foo<s.xVal>( )<< endl; 
   
   return 0;
}

编辑:所以下面有很多讨论,回顾了这里有几个隐含的问题。

“为什么我不能通过将类的成员声明为 constexpr 来强制类的所有实例都为 constexpr?”

举以下例子:

//a.h
struct S;
struct A {std::unique_ptr<S> x; void Foo(); A();/*assume A() tries to instantiate an x*/}

//main.cpp

int main(int argc, char** argv) {
  A a;
  a->foo();
}


//S.h
struct S {
  constexpr S(int x, int y): xVal(x), yVal(y) {}
  constexpr S(int x): xVal(x) {}
  constexpr S() {}

  constexpr int xVal { 0 };         // error!
  constexpr int yVal { 0 };
};

A 和 S 的定义可能位于完全不同的编译单元中,因此 S 必须是 constexpr 的事实可能直到链接时才知道,特别是如果忘记了 A 的实现。这种模棱两可的情况将难以调试且难以实施。更糟糕的是,S 的接口可能完全暴露在共享库、COM 接口等中...这可能完全改变共享库的所有基础设施,这可能是不可接受的。

另一个原因是它的传染性。如果类的任何成员是 constexpr,则所有成员(及其所有成员)和所有实例都必须是 constexpr。假设以下场景:

//S.h
struct S {
  constexpr S(int x, int y): xVal(x), yVal(y) {}
  constexpr S(int x): xVal(x) {}
  constexpr S() {}

  constexpr int xVal { 0 };         // error!
  int yVal { 0 };
};

S 的任何实例都必须是

constexpr
才能独占
constexpr
xval
yVal
本质上变成了
constexpr
,因为
xVal
是。没有技术上的编译器原因让你不能这样做(我不这么认为),但它感觉不太像 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; }
© www.soinside.com 2019 - 2024. All rights reserved.