15

が呼び出される前に静的クラスメンバーが初期化されるという保証はありますmainか?

4

2 に答える 2

11

いいえと思います:

[C++03: 3.6.2/3]: 名前空間スコープのオブジェクトのmain動的初期化(8.5、9.4、12.1、12.6.1)が の最初のステートメントの前に行われるかどうかは実装定義ですの最初のステートメントの後のある時点まで初期化を延期する場合、初期化するmainオブジェクトと同じ翻訳単位で定義された関数またはオブジェクトを最初に使用する前に、初期化を行う必要があります。


うーん、本当に

おそらく、「名前空間スコープで定義された」は「名前空間スコープのオブジェクト」とまったく同じではありません。

[C++03: 9.4.2/2]:クラス定義内のデータ メンバーの宣言はstatic定義ではなく、cv-qualified 以外の不完全な型である可能性がありますvoidデータ メンバーの定義はstatic、メンバーのクラス定義を囲む名前空間スコープに表示されます。名前空間スコープでの定義では、データ メンバーの名前は、演算子staticを使用してそのクラス名で修飾されます。::静的データ メンバーの定義の初期化 式は、そのクラス (3.3.6) のスコープ内にあります。

ただし、クラスのスコープ内にあるのは初期化子です。staticメンバー自体が名前空間スコープ以外のものを持つことについては言及されていません(どこにでも「語彙的に」という言葉を精神的に注入しない限り)。

この楽しい段落あります:

[C++03: 9.4.2/7]:静的データ メンバーは、非ローカル オブジェクトとまったく同じように初期化および破棄されます (3.6.2、3.6.3)。

ただし、残念ながら、main「非ローカル オブジェクト」に関する順序付けと静的初期化の唯一の詳細な定義は、前述の[C++03: 3.6.2/3].


じゃあどうする?

この潜在的にあいまいなルールの意図は、すべてを解決する C++11 の新しい表現によって明確に示されていると思います。

[C++11: 9.4.2/6]:静的データ メンバーは、非ローカル変数とまったく同じように初期化および破棄されます (3.6.2、3.6.3)。

[C++11: 3.6.2/4]:メインの最初のステートメントの前に、静的記憶域期間を持つ非ローカル変数の動的初期化が行われるかどうかは、実装によって定義されます。[..]

于 2013-04-30T12:55:04.143 に答える
4

C++03: つまり、保証なし

C++11: 保証はありません。Lightness の回答を参照してください。

C++03 ステートメントの私の解釈/分析:


用語: [basic.start.init]/1

ゼロ初期化と定数式による初期化をまとめて静的初期化と呼びます。他のすべての初期化は動的初期化です。


非ローカル オブジェクトの初期化の順序:

静的ストレージ期間 (3.7.1) を持つオブジェクトは、他の初期化が行われる前にゼロで初期化されます (8.5)。

ただし、「その他の初期化」がいつ行われるかについては言及されていません。つまり、ゼロ初期化の場合でも、 main の最初のステートメントの前になるという保証はありません。

定数式 (5.19) で初期化された静的ストレージ期間を持つ POD 型 (3.9) のオブジェクトは、動的初期化が行われる前に初期化されなければなりません。

しかし、繰り返しますが、保証はありません。


動的初期化

[basic.start.init]/3

名前空間スコープのオブジェクトの動的初期化 (8.5、9.4、12.1、12.6.1) が main の最初のステートメントの前に行われるかどうかは、実装によって定義されます。初期化が main の最初のステートメントの後のある時点まで延期される場合、初期化されるオブジェクトと同じ翻訳単位で定義された関数またはオブジェクトが最初に使用される前に行われます。

しかし、「名前空間スコープのオブジェクト」とは何ですか? 標準には明確な定義がありません。scopeは、実際には名前のプロパティであり、オブジェクトのプロパティではありません。したがって、これは「名前空間スコープで定義されたオブジェクト」または「名前空間スコープの名前によって導入されたオブジェクト」と読むことができます。動的初期化後の参照「9.4」に注意してください。これは「静的メンバー」を指し、静的データ メンバーのみを意味します。したがって、静的データメンバーは名前空間スコープで定義されるため、「名前空間スコープで定義されたオブジェクト」を意味すると言えます。

[class.static.data]/2

静的データ メンバーの定義は、メンバーのクラス定義を囲む名前空間スコープに表示されます。

この解釈に同意しない場合でも、 [basic.start.init]/1 があります。

同じ翻訳単位の名前空間スコープで定義され、動的に初期化される静的保存期間を持つオブジェクトは、それらの定義が翻訳単位に現れる順序で初期化されます。

これは明らかに静的データ メンバーに適用されます。つまり、静的データ メンバーの定義の前にそのようなオブジェクトがある場合、名前空間スコープの名前によって導入されたオブジェクトとは異なる方法で初期化することはできません。つまり、静的データ メンバーの動的初期化にまったく保証がない場合、名前空間スコープの名前によって導入された先行オブジェクトの保証が適用されます。メインステートメント)。

静的データ メンバーの定義の前にそのようなオブジェクトがなく、解釈に同意ない場合は、静的データ メンバーの動的初期化が保証されません。


結論

したがって、動的な初期化がいつか (使用前に) 発生するという保証と、副作用のある初期化を排除してはならないという例外しかありません。それでも、非ローカル オブジェクトの初期化mainが の最初のステートメントの前に実行されるという保証はありません。


注: 次のような回避策があります。

#include <iostream>

struct my_class
{
    static int& my_var()
    {
        static int i = 42;
        return i;
    }
};

int j = ++my_class::my_var();
int k = ++my_class::my_var();

int main()
{
    std::cout << j << " : " << k << std::endl;
}
于 2013-04-30T14:55:40.110 に答える