c++classc++11structvisual-c++-2015

Uniform initialization on member initializer list error


I am getting a compilation error on this C++11 code but I dont know why. This is the code:

#include <condition_variable>

class NonCopiableClass
{
    std::condition_variable condition_;
};

struct NonCopiableStruct
{
    std::condition_variable condition_;
};

class Test
{
 public:
    Test() : 
        myClass{},
        myStruct{}
    {};
 private:
    NonCopiableClass myClass;
    NonCopiableStruct myStruct;
};

Visual Studio 2015 fails with the following error:

error C2280: 'std::condition_variable::condition_variable(const std::condition_variable &)': attempting to reference a deleted function 1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\mutex(550): note: see declaration of 'std::condition_variable::condition_variable'.

If I change the Test constructor to not use C++11 uniform initialization of the Struct it compiles OK.

Test() : 
        myClass{},
        myStruct() // <--- CHANGE
    {};

I am not getting why for Struct type uses copy constructor but the Class seems OK. It only happends with Struct having non-copiable members.

I also noted that if I init the Struct outside of the Test Class member initializer list It works:

int main()
{
    NonCopiableStruct a{};   
    return 0;
}

Any idea Why is this code failing?. What is going on under the hood? What is the difference between myClass initialization and myStruct initialization?. Why it won't compile if used on the class member initializer list but is it okay I use it outside? I have tried on GCC and it seems to be okay.


Solution

  • This seems like a MSVC bug. The difference is that the struct version is an aggregate, and the class version is not (on account of the default private access specifier).

    The class version is value initialized by {}. The struct version is aggregate initialized. A conforming compiler should just list initialize condition_ with {}, because you didn't provide an initializer for it.

    But MSVC seems to be stumbling on the fact that members of an aggregate are copy initialized from the corresponding initializer in the initializer list. It seems to check for the copy c'tor, even if it isn't supposed to actually use it.

    This is further supported by the fact it knows what to do when an object of the same type is initialized outside of a member initializer list.