13

constexprに関するスライドを読むときの紹介は、「constsを使用した驚くほど動的な初期化」に関するものです。例は

struct S {
    static const int c;
};
const int d = 10 * S::c;
const int S::c = 5;

残念ながら、オーディオトラックが欠落しているので、メモも欠落しているので、ここで何を意味しているのかを推測することしかできません。

以前に定義されdているため、動的に「驚くほど」初期化されるのは正しいですか?S::c d宣言S::cdであるということはおそらく十分ではありません、コンパイラは完全な定義を必要としますね?

そうは言っても、次の例でd 静的に初期化されるのではないかと思います。

struct S {
    static const int c;
};
const int S::c = 5;
const int d = 10 * S::c;  // now _after_ defn of S::c

そして、ケーキを取るために、C ++ 11では、完全な静的初期化のために何が必要でしょうか?constexprS::cdまたは両方?

4

4 に答える 4

3

最初の例でdは、は定数式によって初期化されてS::cいません。

定数式で初期化された、先行初期化を伴う不揮発性constオブジェクト

(C ++ 11 [expr.const] p2、左辺値から右辺値への変換に関する箇条書きを参照)。これは、の初期化がS::cの初期化に先行しないためですd。したがって、静的初期化はS::c(定数式によって初期化されるため)に使用されますが、動的初期化はに使用できますd

静的初期化は動的初期化に先行するため、動的初期化子によってd初期化さ50れます。コンパイラーは、の動的初期化を静的初期化に変換することを許可されていますが、変換する場合、動的初期化を使用できるすべての変数が実際に動的初期化を使用した場合dの値を生成する必要があります。dこの場合、dはいずれかの方法で初期化され50ます。詳細については、C ++ 11[basic.start.init]p2を参照してください。

最初の例に追加して、静的初期化が;constexprに使用されることを保証する方法はありません。dこれを行うには、初期化を並べ替える必要があります。ただし、追加constexprすると最初の例の診断が生成され、少なくとも動的初期化が使用されていないことを確認できます(静的初期化またはコンパイルエラーが発生します)。

2番目のケースを更新して、静的初期化が次のように使用されるようにすることができます。

struct S {
    static const int c; // do not use constexpr here
};
constexpr int S::c = 5;
constexpr int d = 10 * S::c;

constexpr定義ではない変数宣言で使用すること、または初期化子を含まない変数宣言で使用することは不正な形式であるため、constconstexpr定義内で使用する必要はありませんstruct S。この規則には1つの例外があります。これはstatic constexpr、クラス内で指定された初期化子を使用して、リテラルの非整数型のデータメンバーを定義する場合です。

struct T { int n; };
struct U {
    static constexpr T t = { 4 };
};
constexpr T U::t;

この場合、constexprは、初期化子を提供できるようにするためにクラスの定義で使用するconstexpr必要があり、定数式内で使用できるようにするために静的データメンバーの定義で使用する必要があります。

于 2011-12-30T20:37:32.670 に答える
2

静的初期化には、大まかに言えば、定数式初期化子が必要です。

定数式であるためには、大まかに言えば、変数はconst型であり、定数式で先行する初期化を持っている必要があります。

最初の例dでは、の初期化子は定数式ではなく、定数式でもS::cありません(先行する初期化はありません)。したがって、d静的に初期化されません。

2番目の例dでは、の初期化子は定数式であり、すべてがOKです。

私は問題を単純化しています。完全な正式な標準では、これは約9倍長くなります。


指定子に関してconstexprは、オブジェクト宣言する必要はありませんconstexpr。これは単なる追加のエラーチェックです。(これはconstexpr オブジェクトに関するものであり、constexpr 関数に関するものではありません)。

追加のエラー保護が必要な場合は、2番目のバリアントで宣言S::c constexprできます(おそらく5は明日その値を変更し始めますか?)constexpr最初のバリアントに追加しても役に立たない可能性があります。

于 2011-10-02T12:11:25.030 に答える
2

静的初期化がいつ行われるかを決定するために 3.6.2 で規定されているルールには、 の初期化が含まれていないと思います。dしたがって、これが動的初期化です。一方、S::cは確かに静的に初期化されます (5は定数式であるため)。すべての静的初期化は動的初期化の前に行われるため、期待どおりの結果が得られます。

d静的初期化に適格にするには、定数式で初期化する必要があります。S::cこれにより、インラインでの記述が強制されます。

struct S { static constexpr int c = 5; };

const int d = S::c; // statically initialized

標準では、動的初期化を静的初期化に置き換えることが許可されていることに注意してください。これが、元の例で 2 つの行を並べ替えると、2 つの異なる種類の初期化が発生する理由です。TonyK が指摘するようarray[d]に、静的なケースでは使用できますが、動的なケースでは使用できないため、どちらが発生しているかを確認できます。このconstexprアプローチにより、静的な初期化が保証され、オプションのコンパイラの動作に依存する必要がなくなります。

于 2011-10-02T11:54:54.347 に答える
1

配列を宣言することで、定数が静的に初期化されているか動的に初期化されているかを確認できます。

struct S {
    static const int c;
};
const int d = 10 * S::c; // (1)
const int S::c = 5;      // (2)

static char array[d];

dこのコードは動的に初期化されるため、g++ バージョン 4.7.0 では失敗します。そして、(1) と (2) を交換すると、今dは静的に初期化されているため、コンパイルされます。しかし、を使用してそれを修正する別の方法が見つかりませんconstexpr

于 2011-10-02T11:42:38.237 に答える