15

このプログラム:グローバル変数のデストラクタでthread_local変数を初期化することはできますか?

#include <iostream> 

struct Foo { 
    Foo() { 
     std::cout << "Foo()\n"; 
    } 

    ~Foo() { 
     std::cout << "~Foo()\n"; 
    } 
}; 

struct Bar { 
    Bar() { 
     std::cout << "Bar()\n"; 
    } 

    ~Bar() { 
     std::cout << "~Bar()\n"; 
     thread_local Foo foo; 
    } 
}; 

Bar bar; 

int main() { 
    return 0; 
} 

プリント私のために

Bar() 
~Bar() 
Foo() 

(GCC 6.1、Linuxでは、x86-64で)。 〜Foo()は呼び出されません。それは期待された行動ですか?

+0

法的に認められているかどうか、なぜあなたはそれをしますか? –

+3

@DavidHaim私は 'libC++ abi'(特に' __cxa_thread_atexit() 'の一部を実装しようとしていますが、このケースを処理するかどうかは不思議でした。 –

+5

これはたぶん 'cout'が' foo'の前に破壊されたことでしょう。 'Foo'のデストラクタから例外をスローし、' std :: terminate'が呼び出されているかどうか確認してください。 –

答えて

9

標準ではこのケースをカバーしていません。最も厳密には、静的な記憶期間を持つオブジェクトのデストラクタでthread_localを初期化することは合法ですが、プログラムが正常に完了することを許可することはできません。

問題が[basic.start.term]で発生:

1 - デストラクタ([class.dtor])を初期化目的のために(すなわち、その寿命([basic.life])オブジェクトが開始され、である)静的記憶期間とmainから戻り、std :: exit([support.start.term])を呼び出した結果として呼び出されます。指定されたスレッド内のスレッド記憶期間を持つ初期化されたオブジェクトのデストラクタは、そのスレッドの初期関数から戻り、そのスレッドがstd :: exitを呼び出した結果として呼び出されます。そのスレッド内のスレッド記憶期間を有する全ての初期化されたオブジェクトに対するデストラクタの完了は、静的記憶期間を有するオブジェクトのデストラクタの開始前に順序付けられる。 [...]

のでbar::~Bar::foo::~Fooの完了は矛盾であるbar::~Barの開始、前に配列決定されます。

ザ・取得のみアウトは[basic.start.term]/1のみが一生のプログラム/スレッドの終了の時点で始まっているオブジェクトに適用されると主張するかもしれないが、コントラ[stmt.dcl]があります

5 - 静的またはスレッド記憶期間を持つブロックスコープオブジェクトのデストラクタは、作成された場合にのみ実行されます。 [注意:[basic.start.term]は、静的およびスレッド記憶期間を持つブロックスコープオブジェクトが破棄される順序を記述します。 - end note]

これは、通常のスレッドとプログラムの終了、メインからの戻り、またはスレッド関数からの呼び出し、またはstd::exitの呼び出しによってのみ適用されることを明確に意図しています。

また、[basic.stc.thread]を有する:

スレッド貯蔵期間を持つ変数は、その最初のODR使用前に初期化されなければならない構成されている場合([basic.def.odr])と、スレッドの終了時に破棄されなければなりません。

ここで、 "shall"は、ユーザではなく、実装者への指示です。

[basic.start.term]/2は適用されないため(以前は破棄されていないため)、デストラクタスコープthread_localの有効期間の開始に間違いはありません。そのため、プログラムが正常に完了することを許可すると、未定義の動作が発生すると私は信じています。

静的対静的ストレージの継続時間については、thread_localではなく静的であるにもかかわらず、以前同様の質問がありました。 Destruction of objects with static storage duration(およびhttps://groups.google.com/forum/#!topic/comp.std.c++/Tunyu2IJ6w0)、およびDestructor of a static object constructed within the destructor of another static objectである。後者の質問でJames Kanzeに同意する傾向がありますが、標準で定義されていないため、動作は未定義です。[defns.undefined]が適用されます。最善の方法は、欠陥報告を開こうとしている人(static秒とthread_local秒のデストラクタで初期化されたすべての組み合わせをカバーしている人のためであり、決定的な答えを期待するためです。

+0

それは私のためにそれを明確にします! –

関連する問題