I stumbled upon what I think is a very easy way to unknowingly shoot yourself in the foot.
first a little introduction
The initialization order of data members is the order of declaration of the data members. So this is illegal:
struct A
{
    std::size_t i_;
    std::size_t length_;
    A(std::size_t length)
      : i_{length_} // UB here. `length_` is uninitialized
        length_{length}
    {}
};
Because data member length_ is uninitialized when used in the initializer of i_. Fortunately both gcc and clang give a very nice warning for this. The simple solution is to initialize each data members from the parameters, i.e. i_{length}.
now to the main point
But how about when it is not immediately obvious. E.g. when a data member is a 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?
        // ..
    }
};
The same situation arises when using data member initializer:
struct X
{
    std::thread thread_{&X::worker_thread, this};
    std::mutex mutex_;
};
This looks very innocent and neither gcc and clang warn on this scenario. This is not surprising, as the dependency is hidden.
I would immagine the above scenario is not uncommon so I am looking on confirmation that this is indeed UB. And declare the std::mutex data member last, or default initialize it and assign it later.
Yes, it is indeed undefined behavior. As a matter of fact, you have over-complicated the example with threads and mutexes. Every time you are using this in initialization of members (explicitly or implicitly), you are opening yourself to troubles. Easier example:
struct A {
    int y;
    int x = 0;
    A() : y(sety()) { }
    int sety() { return x; } // Ka-boom!
};
It is always quite dangerous to call non-static member functions from within member initialization; and one generally has also to be careful when calling member functions from constructor body.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With