Explicit default constructors and copy-list-initialization
9.4.5 [dcl.init.list]
Richard Smith

Created on 2012-07-08.00:00:00 last changed 49 months ago


Date: 2015-10-15.00:00:00

Proposed resolution (October, 2015):

Change [over.match.ctor] paragraph 1 as follows:

When objects of class type are direct-initialized (9.4 [dcl.init]), copy-initialized from an expression of the same or a derived class type (9.4 [dcl.init]), or default-initialized (9.4 [dcl.init]), overload resolution selects the constructor. For direct-initialization or default-initialization, the candidate functions are all the constructors of the class of the object being initialized. For 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.
Date: 2015-11-10.00:00:00

Additional note, October, 2015:

It has been suggested that the resolution of issue 1630 went too far in allowing use of explicit constructors for default initialization, and that default initialization should be considered to model copy initialization instead. The resolution of this issue would provide an opportunity to adjust that.

Date: 2015-05-15.00:00:00

Proposed resolution (May, 2015): [SUPERSEDED]

This issue is resolved by the resolution of issue 1630: default initialization now uses [over.match.ctor], which now permits explicit constructors for default-initialization.

Date: 2016-06-15.00:00:00

[Adopted at the June, 2016 meeting as paper P0398R0.]

Consider the following example:

  struct A {
   explicit A() = default;

  struct B : A {
   explicit B() = default;

  struct C {
   explicit C();

  struct D : A {
   C c;
   explicit D() = default;

  template<typename T> void f() {
   T t = {};
  template<typename T> void g() {
   void x(T t);

The question is whether f<B>, f<C>, f<D>, g<B>, g<C>, and g<D> are well-formed or ill-formed.

The crux here is whether [over.match.list] is the governing law in each of these cases. If it is, the initialization is ill-formed, because copy-list-initialization has selected an explicit constructor. The standard seems clear that f<A> and g<A> are valid (because A is an aggregate, so [over.match.list] is not reached nor applicable), f<B> is valid (because value-initialization does not call the default constructor, so [over.match.list] is not reached), and that g<B>, g<C>, and g<D> are ill-formed (because [over.match.list] is reached from [over.ics.list] and selects an explicit constructor). The difference between f<B> and g<B> is troubling.

For f<C> and f<D>, it's not clear whether the default constructor call within value-initialization within list-initialization uses [over.match.list] — but some form of overload resolution seems to be implied, since presumably we want to apply SFINAE to variadic constructor templates, diagnose classes which have multiple default constructors through the addition of default arguments, and the like.

It has been suggested that perhaps we are supposed to reach [over.match.list] for an empty initializer list for a non-aggregate class with a default constructor only when we're coming from [over.ics.list], and not when 9.4.5 [dcl.init.list] delegates to value-initialization. That would make all the fs valid, but g<B>, g<C>, and g<D> ill-formed. [class.conv.ctor] paragraph 2 says explicit constructors are only used for direct-initialization or casts, which argues for at least f<C>, f<D>, g<C> and g<D> being ill-formed.

See also issue 2116.

Date User Action Args
2017-02-06 00:00:00adminsetstatus: review -> cd4
2015-11-10 00:00:00adminsetmessages: + msg5624
2015-11-10 00:00:00adminsetmessages: + msg5623
2015-05-25 00:00:00adminsetmessages: + msg5476
2015-05-25 00:00:00adminsetstatus: drafting -> review
2012-11-03 00:00:00adminsetstatus: open -> drafting
2012-07-08 00:00:00admincreate