This code compiles fine with GCC 5.X, MSVC, but GCC 6.X gives error:
\"converting to \'a\' from initializer list would use explicit constructor \'a:
b
is an aggregate. When you initialize it using an initializer list, the elements in the list will initialize the first n members of the aggregate, where n is the number of elements in the list. The remaining elements of the aggregate are copy-list-initialized.
So in your example, c
will be copy-list-initialized, but that is ill-formed if the chosen constructor is explicit
, hence the error.
The relevant standard quotes are
[dcl.init.aggr]/3
When an aggregate is initialized by an initializer list as specified in [dcl.init.list], the elements of the initializer list are taken as initializers for the elements of the aggregate. The explicitly initialized elements of the aggregate are determined as follows:
...
— If the initializer list is an initializer-list, the explicitly initialized elements of the aggregate are the first n elements of the aggregate, where n is the number of elements in the initializer list.
— Otherwise, the initializer list must be{}
, and there are no explicitly initialized elements.
[dcl.init.aggr]/5
For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:
...
— Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
The effect of copy initializing c
from an empty initializer list is described in
[dcl.init.list]/3
List-initialization of an object or reference of type
T
is defined as follows:
...
— Otherwise, if the initializer list has no elements andT
is a class type with a default constructor, the object is value-initialized.
[dcl.init]/8
To value-initialize an object of type
T
means:
...
— ifT
is a (possibly cv-qualified) class type with either no default constructor ([class.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;
[dcl.init]/7
To default-initialize an object of type
T
means:
— If T is a (possibly cv-qualified) class type, constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one for the initializer () is chosen through overload resolution. The constructor thus selected is called, with an empty argument list, to initialize the object.
[over.match.ctor]
... For copy-initialization, the candidate functions are all the converting constructors of that class.
[class.conv.ctor]/1
A constructor declared without the function-specifier
explicit
specifies a conversion from the types of its parameters (if any) to the type of its class. Such a constructor is called a converting constructor.
In the example above, a
has no converting constructors, so overload resolution fails. The (non-normative) example in [class.conv.ctor]/2 even contains a very similar case
struct Z { explicit Z(); explicit Z(int); explicit Z(int, int); }; Z c = {}; // error: copy-list-initialization
You can avoid the error by providing a default member initializer for c
struct b
{
a c{}; // direct-list-initialization, explicit ctor is OK
};