2

I learnt about the explicit keyword in c++ and its uses. Then for practice I wrote the following program that works with MSVC but not with gcc and clang. Demo.

class Testing
{
    public:
        explicit Testing() = default;
}; 
int main()
{
    Testing t[2] = {};  //works with msvc but not with gcc and clang
}

As you can see, the above works with msvc but not with clang and gcc. I am using C++20 and want to know which compiler is correct for this example according to the C++20 standard.


GCC produces the following error:

<source>:8:21: error: converting to 'Testing' from initializer list would use explicit constructor 'constexpr Testing::Testing()'
    8 |     Testing t[2] = {};  //works with msvc but not with gcc and clang
      |                     ^
Jason
  • 36,170
  • 5
  • 26
  • 60
Alex
  • 318
  • 1
  • 14

1 Answers1

4

The program is ill-formed and gcc and clang are correct in rejecting the program because Testing t[2]={}; is copy-list-initialization and since t is an array it uses aggregate initialization which in turn result in copy initialization of t's member(s) using the empty initializer list {} which fails as the default ctor is explicit(and so Testing::Testing() cannot be used in copy initialization).

This is explained in detail below.

From list initialization:

3) List-initialization of an object or reference of type T is defined as follows:

3..4) Otherwise, if T is an aggregate, aggregate initialization is performed

This means that since T in our example is Testing[2] which is an array(and so an aggregate), aggregate initialization will be performed.

Next, from aggregate initialization:

5) For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:

5.4) Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list.

This means that the array elements(which are not explicitly initialized btw) will be copy initialized from an empty initializer list {}. What this in turn means is that it is as if for each element of the array we're writing Testing array_element_nth = {}; which results in value-initialization as per dcl.init.list#3.5:

Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.


Next, from value initialization:

To value-initialize an object of type T means:

  • if T has either no default constructor ([class.default.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;

This means that the array element will be default initialized.

So we move onto default initialization:

To default-initialize an object of type T means:

  • If T is a (possibly cv-qualified) class type ([class]), constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one for the initializer () is chosen through overload resolution ([over.match]). The constructor thus selected is called, with an empty argument list, to initialize the object.

This means that that the constructors will be enumerated and best of them will be chosen with (). So we move onto over.match.ctor:

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 that is not in the context of copy-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization (including default initialization in the context of copy-initialization), 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.

This means that the default ctor will be used but since this is in copy initialization context, the explicit ctor cannot be used.

Thus, msvc is wrong in accepting the program.


Here is the msvc bug:

MSVC compiles invalid program involving explicit constructor

Jason
  • 36,170
  • 5
  • 26
  • 60