3
// File: InitFirst.h

#pragma once

template <int val>
struct InitFirst
{
    static float s_dividedByThree;
};

template <int val>
float InitFirst<val>::s_dividedByThree = val / 3.0;

// File: Test.h

#include <conio.h>
#include <tchar.h>

#include "InitFirst.h"

float g_shouldBeOneThird = InitFirst<1>::s_dividedByThree;

int _tmain(int argc, _TCHAR* argv[])
{
    _cprintf("%f\n", g_shouldBeOneThird);
    getch();
    return 0;
}

g_shouldBeOneThird は 0.333 前後に初期化されることが保証されていますか? つまり、静的に初期化された InitFirst<1>::s_dividedByThree は、g_shouldBeOneThird を静的に初期化するために使用されるまでに初期化されることが保証されていますか?

4

1 に答える 1

3

標準(3.6.2)から:

静的ストレージ期間 (3.7.1) を持つオブジェクトは、他の初期化が行われる前にゼロで初期化されます (8.5)。静的な保存期間を持つ参照と、静的な保存期間を持つ POD 型のオブジェクトは、定数式 (5.19) で初期化できます。これは、定数の初期化と呼ばれます。ゼロ初期化と定数初期化をまとめて静的初期化と呼びます。他のすべての初期化は動的初期化です。静的初期化は、動的初期化が行われる前に実行されます。オブジェクトの動的初期化は、順序付きまたは順序なしのいずれかです。明示的に特殊化されたクラス テンプレートの静的データ メンバーの定義には、初期化の順序があります。その他のクラス テンプレートの静的データ メンバー (つまり、暗黙的または明示的にインスタンス化された特殊化) には、順序付けされていない初期化があります。名前空間スコープで定義された他のオブジェクトには、初期化の順序がありました。単一の翻訳単位内で定義され、順序付けされた初期化を持つオブジェクトは、翻訳単位での定義の順序で初期化されます。初期化の順序は、順序付けされていない初期化のオブジェクトと、異なる翻訳単位で定義されたオブジェクトでは規定されていません。

ここではfloat InitFirst<val>::s_dividedByThree、定数式で初期化しているため、これは動的初期化 ( fx float g_shouldBeOneThird) の前に発生します。この単純化された例は、動的な初期化を行う場合の単純化である可能性があると感じていますが、関連する部分は次のとおりです。翻訳単位」。

グローバル変数 (一種) を使用するまでに確実に初期化するためのトリックがあります。秘訣は、それらをグローバル関数の静的ローカル変数として保持することです。ローカル静的変数は最初にアクセスされたときに初期化されるため、初期化の順序は問題になりません。

template <int val>
struct InitFirst
{
    static float & s_dividedByThree();
};

template <int val>
float & InitFirst<val>::s_dividedByThree(){
    static float staticVariable = val / 3.0;
    return staticVariable;
}

その後、ほとんど以前と同じようにこれらの変数にアクセスできます。

float g_shouldBeOneThird = InitFirst<1>::s_dividedByThree();

ただし、ローカルの静的変数の初期化は、複数のスレッドの下では安全ではないことに注意してください (標準では安全であるべきではありません)。それが懸念される場合は、いくつかのロックで初期化を保護することをお勧めします。もちろん、コンパイラは安全なコードを生成することができます。これは、デフォルトで gcc が行うことです (おそらく他のコンパイラも同様です)。

于 2012-04-04T12:31:00.600 に答える