c++language-lawyerlist-initialization

Copy-list-initialization from empty braces with explicit default constructor


Why do seemingly all compilers reject the following program? (https://godbolt.org/z/EK8zW34nY)

struct A
{
    explicit A() {}
};

int main()
{
    A a = {};
}

a should be value-initialized per [dcl.init.list]/3.5 from what I can tell and that should in turn select a constructor as if to default-initialize a Default-initialization does not exclude explicit constructors.


This is a follow-up from a discussion with another user in another question:

Would one apply [over.match.list], which I do not think should apply here, then it would fail because of "In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.".

When modifying the program to

struct A
{
    explicit A() {}

    template<typename = void>
    A() {}
};

int main()
{
    A a = {};
}

then GCC and Clang still reject it with error messages hinting that they consider it that way, while EDG and MSVC accept, choosing the templated constructor as if there was "copy-default-initialization" which excludes explicit constructors completely. (https://godbolt.org/z/xbzjhYYhM)


Solution

  • When you do

    A a = {};
    

    You are using copy-list-initialization which brings in [over.match.ctor] which states:

    When objects of class type are direct-initialized, copy-initialized from an expression of the same or a derived class type ([dcl.init]), or default-initialized, overload resolution selects the constructor. For direct-initialization or default-initialization (including default-initialization in the context of copy-list-initialization), the candidate functions are all the constructors of the class of the object being initialized. Otherwise, the candidate functions are all the converting constructors ([class.conv.ctor]) of that class. The argument list is the expression-list or assignment-expression of the initializer. For default-initialization in the context of copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.

    And since the object is not being initialized from an expression of the same or derived type the second half applies and last part of the last sentence makes the code ill formed.


    This makes sense since {} isn't an A, its an implicit object creator and you cant make an implicit object from a type that has an explicit default constructor.