C++11 では、ロックの必要がなくなりました。静的ローカル変数が既に初期化されている場合、同時実行は待機します。
§6.7 [stmt.dcl] p4
変数の初期化中に制御が同時に宣言に入った場合、同時実行は初期化の完了を待ちます。
C++03 の場合、次のようになります。
§6.7 [stmt.dcl] p4
静的ストレージ期間 (3.7.1) を持つすべてのローカル オブジェクトのゼロ初期化 (8.5) は、他の初期化が行われる前に実行されます。定数式で初期化された静的ストレージ期間を持つ POD タイプ (3.9) のローカル オブジェクトは、そのブロックが最初に入力される前に初期化されます。実装は、実装が名前空間スコープ (3.6.2) で静的ストレージ期間を持つオブジェクトを静的に初期化することを許可されているのと同じ条件下で、静的ストレージ期間を持つ他のローカル オブジェクトの早期初期化を実行することが許可されています。それ以外の場合、そのようなオブジェクトは、最初にコントロールがその宣言を通過するときに初期化されます。
最後の部分は、コードに適用されるため重要です。コントロールが最初に に入るget_class_instance()
と、最初にクリティカル セクションの初期化を通過し、次にシングルトンの宣言を通過し (そのため、クリティカル セクション内で初期化されます)、クリティカル セクションの初期化解除を通過します。
したがって、理論的な観点からは、コードは安全でなければなりません。
ただし、すべての関数呼び出しでクリティカルセクションに入らないように、これを改善できます。@Chethan の基本的な考え方はしっかりしているので、それをベースにします。ただし、動的割り当ても回避します。ただし、そのためには Boost.Optional に依存しています。
#include <boost/optional.hpp>
Class& get_class_instance() {
static boost::optional<Class> c;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!c)
c = Class(data);
LeaveCriticalSection(&cs);
inited = true;
}
return *c;
}
Boost.Optional はデフォルトの初期化を回避し、二重チェックはすべての関数呼び出しでクリティカル セクションに入ることを回避します。ただし、このバージョンでClass
は、代入でのコピー コンストラクターへの呼び出しが導入されています。その解決策は、インプレース ファクトリです。
#include <boost/utility/in_place_factory.hpp>
#include <boost/optional.hpp>
Class& get_class_instance() {
static boost::optional<Class> c;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!c)
c = boost::in_place(data);
LeaveCriticalSection(&cs);
inited = true;
}
return *c;
}
@R に感謝します。この最終的なソリューションに協力した Martinho Fernandes と @Ben Voigt。このプロセスに興味がある場合は、気軽にトランスクリプトをご覧ください。
ここで、コンパイラが C++11 機能の一部を既にサポートしているが、静的初期化機能をサポートしていない場合はstd::unique_ptr
、placement new と静的に整列されたバッファーを組み合わせて使用することもできます。
#include <memory> // std::unique_ptr
#include <type_traits> // alignment stuff
template<class T>
struct destructor{
void operator(T* p) const{
if(p) // don't destruct a null pointer
p->~T();
}
};
Class& get_class_instance() {
typedef std::aligned_storage<sizeof(Class),
std::alignment_of<Class>::value>::type storage_type;
static storage_type buf;
static std::unique_ptr<Class, destructor> p;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!p)
p.reset(new (&buf[0]) Class(data));
LeaveCriticalSection(&cs);
inited = true;
}
return *p;
}