c – データメンバスレッドとミューテックスの初期化間違った順序は未定義の動作をしますか?

無意識のうちに自分の足で撃つのがとても簡単な方法だと私が思うことにつまずいた。

はじめに少し紹介

データメンバの初期化順序は、データメンバの宣言の順序です。だからこれは違法です:

struct A
{
    std::size_t i_;
    std::size_t length_;

    A(std::size_t length)
      : i_{length_} // UB here. `length_` is uninitialized
        length_{length}
    {}
};

データメンバlength_は、i_のイニシャライザで使用されると初期化されないためです。幸いなことに、gccとclangの両方がこれについて非常に良い警告を出します。簡単な解決策は、パラメータから各データメンバを初期化すること、すなわち、i_ {長さ}である。

今要点に

しかし、それがすぐにはっきりしない場合はどうでしょう。例えば。データメンバがstd :: threadの場合

struct X
{
    std::thread thread_;
    std::mutex mutex_;

    X() : thread_{&X::worker_thread, this}
    {}

    auto worker_thread() -> void
    {
        // use mutex_  
        std::lock_guard lk{mutex_}; // boom?
        // ..
    }
};

データメンバー初期化子を使用するときにも同じ状況が発生します。

struct X
{
    std::thread thread_{&X::worker_thread, this};
    std::mutex mutex_;
};

これは非常に罪のないように見えますし、gccもclangもこのシナリオについて警告していません。依存関係が隠されているので、これは驚くべきことではありません。

上記のシナリオは珍しいことではないと思いますので、これがUBであることの確認を検討しています。そして、std :: mutexデータメンバを最後に宣言するか、デフォルトで初期化して後で割り当てます。

ベストアンサー
はい、それは確かに未定義の動作です。実際のところ、あなたは例をスレッドとミューテックスで過度に複雑にしました。メンバーの初期化に(明示的または暗黙的に)これを使用しているたびに、あなたはトラブルに自分自身を開いています。より簡単な例:

struct A {
    int y;
    int x = 0;
    A() : y(sety()) { }

    int sety() { return x; } // Ka-boom!
};

メンバ初期化内から非静的メンバ関数を呼び出すことは常に非常に危険です。また、コンストラクタ本体からメンバ関数を呼び出すときにも一般的に注意する必要があります。

転載記事の出典を記入してください: c – データメンバスレッドとミューテックスの初期化間違った順序は未定義の動作をしますか? - コードログ