constexpr
関数から計算されたconstexpr
値(つまり、コンパイル時の定数)が必要です。そして、これらの両方をクラスの名前空間、つまりクラスの静的メソッドと静的メンバーにスコープします。
私は最初にこれを(私にとって)明白な方法で書きました:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
g++-4.5.3 -std=gnu++0x
は次のように述べています。
error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression
g++-4.6.3 -std=gnu++0x
の不満:
error: field initializer is not constant
OK、私はおそらく、私は物事をクラスの本体から移動させなければならないと考えました。だから私は次のことを試しました:
class C2 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));
g++-4.5.3
は文句なしにコンパイルします。残念ながら、他のコードでは範囲ベースのfor
ループを使用しているため、少なくとも4.6が必要です。 サポートリスト を詳しく見ると、constexpr
も4.6が必要になるようです。 g++-4.6.3
で
3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]
これは私には本当に奇妙に聞こえます。ここではどのように「constexpr
」が異なるのですか? -fpermissive
を追加する気はありません。他のコードを厳密にチェックすることを好みます。 foo
実装をクラス本体の外側に移動しても、目に見える効果はありませんでした。
誰かがここで何が起こっているのか説明できますか?私がやろうとしていることをどのように達成できますか?私は主に次の種類の回答に興味があります。
他の有用な回答も歓迎しますが、おそらく簡単に受け入れられないでしょう。
この規格には以下が必要です(セクション9.4.2):
リテラル型の
static
データメンバーは、constexpr
指定子を使用してクラス定義で宣言できます。そうである場合、その宣言はbrace-or-equal-initializerを指定するものとし、すべてのinitializer-clauseつまりassignment-expressionは定数式です。
「2回目の試行」とイリヤの答えのコードでは、宣言にbrace-or-equal-initializerはありません。
最初のコードは正しいです。 gcc 4.6がそれを受け入れていないのは残念ですが、4.7.xを便利に試す場所はわかりません(たとえば、ideone.comはgcc 4.5に留まっています)。
残念ながら、標準ではクラスが完全なコンテキストで静的constexpr
データメンバーを初期化できないため、これは不可能です。 9.2p2のbrace-or-equal-initializersの特別なルールは、非静的データメンバーにのみ適用されますが、これは1つは静的です。
この理由として最も可能性が高いのは、constexpr
変数をメンバー関数の本体内からコンパイル時の定数式として使用できる必要があるため、変数初期化子が関数本体の前に完全に定義されていることです。初期化子のコンテキストではまだ不完全(未定義)であり、このルールが作動して、式が定数式ではなくなります。
constexpr
関数またはconstexpr
コンストラクターの定義外の未定義のconstexpr
関数または未定義のconstexpr
コンストラクターの呼び出し。
考慮してください:
class C1
{
constexpr static int foo(int x) { return x + bar; }
constexpr static int bar = foo(sizeof(int));
};
1)Ilyaの例は、static constexpr data member barが標準外の次のステートメントに違反して行外で初期化されるという事実に基づく無効なコードである必要があります。
9.4.2 [class.static.data] p3:... constexpr指定子を使用して、リテラル型の静的データメンバーをクラス定義で宣言できます。その場合、その宣言は、brace-or-equal-initializerを指定するものとします。このイニシャライザでは、assignment-expressionであるすべてのinitializer-clauseが定数式です。
2)MvGの質問のコード:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
静的メンバーfoo(int)はbarが開始します(トップダウン処理を想定)。いくつかの事実:
constexpr関数またはconstexprコンストラクターの定義外の、未定義のconstexpr関数または未定義のconstexprコンストラクターの呼び出し。
class C1
{
constexpr static int foo() { return bar; }
constexpr static int bar = foo();
};
無効に見えますが、fooがbar。ロジックは次のとおりです。
しかし、(5.19 p2)の箇条書き9により、barしないはまだ初期化されていないため満足します。
- 以下に適用されない限り、左辺値から右辺値への変換(4.1):
- 定数式で初期化された、先行する初期化を持つ不揮発性constオブジェクトを参照する整数型または列挙型のglvalue。
したがって、barの左辺値から右辺値への変換では、(9.4.2 p3)の要件を満たさない定数式は生成されません。
関数呼び出し置換(7.1.5)で置換されたときに定数式を生成しない引数を持つconstexpr関数の呼び出し
#include <iostream>
class C1
{
public:
constexpr static int foo(constexpr int x)
{
return x + 1;
}
static constexpr int bar;
};
constexpr int C1::bar = C1::foo(sizeof(int));
int main()
{
std::cout << C1::bar << std::endl;
return 0;
}
このような初期化はうまく機能しますが、clangでのみ有効です
おそらく、ここでの問題は、クラス内の宣言/定義の順序に関連しています。ご存知のように、クラスで宣言/定義される前であっても、任意のメンバーを使用できます。
クラスでde constexpr値を定義する場合、コンパイラはクラス内にあるため、使用できるconstexpr関数がありません。
おそらく、このアイデアに関連する Philip answerは、質問を理解するための良い点です。
問題なくコンパイルされる次のコードに注意してください。
constexpr int fooext(int x) { return x + 1; }
struct C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = fooext(5);
};
constexpr static int barext = C1::foo(5);