【问题标题】:Why can't non-static data members be constexpr?为什么非静态数据成员不能是 constexpr?
【发布时间】:2014-12-10 19:01:28
【问题描述】:

这是有效的代码:

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 };
};

但在这里我真的很想声明xValyVal 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。但是为什么

【问题讨论】:

  • 因为它们是在运行时初始化的成员变量,在初始化之前无法评估。
  • @remyabel 我相信为什么我不能拥有 constexpr 与为什么我在不使用大括号初始化时收到 constexpr 错误的原因不同......
  • @IdeaHat 你看过答案了吗?
  • @remyabel:这个问题完全不同。它与初始化静态数据成员无关。有没有办法让我的问题顶部的误导性链接消失?
  • @KnowItAllWannabe 我将其标记为不具建设性,评论真空最终会吹走所有的 cmets。

标签: c++ c++11 constexpr


【解决方案1】:

想想constexpr 的含义。这意味着我可以在编译时解析这个值。

因此,类的成员变量本身不能是constexpr...xVal所属的实例直到实例化时才存在!拥有xVal 的东西可能是constexp,这将使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 xvalyVal 本质上变成了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; }

【讨论】:

  • 我希望 constexpr 数据成员会产生一个类,其中所有实例都可以声明为 constexpr。也就是说,在编译期间尝试创建具有非 constexpr 值的对象将被拒绝。
  • @KnowItAllWannabe constexpr 不是对如何使用某些东西的限制;相反,它是在只允许使用 constexpr 的地方使用它的许可。给定非 constexpr 参数的 constexpr 函数将在运行时运行,而不是在编译时运行……这是一件好事,因为我不想为每种情况重复我的代码(就像我以前必须做的那样)。任何可以实例化为 constexpr 的类总是可以有不是 constexpr 的实例(我需要做的就是调用 new S)。这有意义吗?
  • 由于对象的可变数据成员声明为constexpr,甚至可以说constexpr 表示编译时已知的值constexpr on types 确实是一个限制;可以想象constexpr 数据成员需要使用常量表达式进行初始化。可能是收益太小,或者违背了constexpr对非静态数据成员的初衷。
  • @IdeaHat:这是有道理的,我理解这一点,但我认为确保类的所有实例在编译期间都具有已知值也是有道理的。
  • @KnowItAllWannabe 我不相信这是可能的,而且,就 /u/dyp 而言,我没有看到该实用程序。要求所有对象都是 constexpr 感觉就像单例模式的所有问题一样......它让我,作为该类的用户,无法做很多事情,以便为我提供一个对我来说微不足道的功能我会根据具体情况提供。
猜你喜欢
  • 2016-07-03
  • 1970-01-01
  • 1970-01-01
  • 2013-10-19
  • 1970-01-01
  • 2015-11-28
  • 2016-07-29
  • 2021-12-07
相关资源
最近更新 更多