std::cout
静的記憶域期間を持つオブジェクトのコンストラクタでは、オブジェクトを安全に使用できるようです。質問。
ただし、変数テンプレートの場合、これらを安全に使用できるかどうかは完全にはわかりません。
#include <iostream>
template<class T>
T x = T{};
void foo()
{
class Test
{
public:
Test() { std::cout << "Test::Test\n"; }
};
Test t = x<Test>;
}
int main()
{
std::cout << "main\n";
}
このコードはclangでクラッシュします(ライブ例) バグかどうかはわかりません。
ベストアンサー1
その質問で説明されているように、
#include <iostream>
グローバル変数を定義するのと同じである
static std::ios_base::Init __init;
これは(TUの先頭にそれを含めると仮定すると)すべての静的記憶期間オブジェクトに対して、順序付けられました同じ TU での初期化では、ストリーム オブジェクトが設定されています。
しかし、明示的および暗黙的にインスタンス化されたテンプレートの特殊化は順序なし初期化([基本.開始.ダイナミック]/1)1 :
静的記憶域期間を持つ非ローカル変数の動的初期化は、変数が暗黙的または明示的にインスタンス化された特殊化である場合は順序付けされず、それ以外の場合は順序付けされます[注記省略] 単一の翻訳単位内で順序付き初期化が定義された変数は、翻訳単位内での定義順に初期化されるものとする。
それ以来
プログラムがスレッドを開始すると、その後の変数の順序なし初期化は、他のすべての動的初期化に対して順序付けされません。それ以外の場合、変数の順序なし初期化は、他のすべての動的初期化に対して不確定に順序付けされます。
変数テンプレートの特殊化が初期化された時点でストリーム オブジェクトが初期化されているという保証はありませんx<Test>
。
この場合、実行可能なものの1つが未定義の動作(ストリームオブジェクトを初期化前に使用)につながるため、プログラム全体の動作は未定義になります([イントロ.実行]/5)。
修正方法は、のコンストラクターstd::ios_base::Init
でオブジェクトを自分で構築することです。Test
1これは、C++14 が公開された時点では変数テンプレートに対して実際には十分に指定されていませんでしたが、常にそのように意図されていました。