web-dev-qa-db-ja.com

非静的データメンバーをconstexprにできないのはなぜですか?

これは有効なコードです:

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

しかし、ここで私は本当にxValyValconstexprを次のように宣言したいと思います。

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として宣言される可能性があるためです。しかしなぜ

27

constexprの意味を考えてください。これは、コンパイル時にこの値を解決できることを意味します。

したがって、クラスのメンバー変数自体をconstexprにすることはできません... xValが属するインスタンスはインスタンス化時まで存在しません! xValを所有するものはconstexpであり、xValconstexprになりますが、xValconstexpr自体。

これは、これらの値を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 };
};

排他的にconstexprconstexprを保持できるようにするには、Sのインスタンスはxvalである必要があります。 yValconstexprであるため、本質的に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; }
22
IdeaHat