Title
Multiple default constructor templates
Status
cd4
Section
9.4 [dcl.init]
Submitter
Nikolay Ivchenkov

Created on 2013-03-01.00:00:00 last changed 87 months ago

Messages

Date: 2014-11-15.00:00:00

[Moved to DR at the November, 2014 meeting.]

Date: 2014-06-15.00:00:00

Proposed resolution (June, 2014):

  1. Change 6.3 [basic.def.odr] paragraph 3 as follows:

  2. ...An assignment operator function in a class is odr-used by an implicitly-defined copy-assignment or move-assignment function for another class as specified in 11.4.5.3 [class.copy.ctor]. A default constructor for a class is odr-used by default initialization or value initialization as specified in 9.4 [dcl.init]. A constructor for a class is odr-used as specified in 9.4 [dcl.init]. A destructor for a class is odr-used if it is potentially invoked (11.4.7 [class.dtor]).
  3. Change 9.4 [dcl.init] paragraph 7 as follows:

  4. To default-initialize an object of type T means:

    • if If T is a (possibly cv-qualified) class type (Clause 11 [class]), the default constructor (11.4.5 [class.ctor]) for T is called (and the initialization is ill-formed if T has no default constructor or overload resolution (12.2 [over.match]) results in an ambiguity or in a function that is deleted or inaccessible from the context of the initialization); constructors are considered. The applicable constructors are enumerated (12.2.2.4 [over.match.ctor]), and the best one for the initializer () is chosen through overload resolution (12.2 [over.match]). The constructor thus selected is called, with an empty argument list, to initialize the object.

    • if If T is an array type, each element is default-initialized;.

    • otherwise Otherwise, no initialization is performed.

  5. Change 11.4.5 [class.ctor] paragraph 4 as follows:

  6. A default constructor for a class X is a constructor of class X that can be called without an argument either has no parameters or else each parameter that is not a function parameter pack has a default argument. If there is no user-declared constructor...
  7. Change 12.2 [over.match] bullet 2.4 as follows:

  8. Overload resolution selects the function to call in seven distinct contexts within the language:

    • ...

    • invocation of a constructor for default- or direct-initialization (9.4 [dcl.init]) of a class object (12.2.2.4 [over.match.ctor]);

    • ...

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

  10. When objects of class type are direct-initialized (9.4 [dcl.init]), or copy-initialized from an expression of the same or a derived class type (9.4 [dcl.init]), or default-initialized, 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 (11.4.8.2 [class.conv.ctor]) of that class. The argument list is the expression-list or assignment-expression of the initializer.
Date: 2013-03-01.00:00:00

It is unclear whether code like the following is supposed to be supported or not:

  #include <iostream>
  #include <type_traits>

  #define ENABLE_IF(...) \
    typename std::enable_if<__VA_ARGS__, int>::type = 0
  #define PRINT_VALUE(...) \
    std::cout << #__VA_ARGS__ " = " << __VA_ARGS__ << std::endl

  struct undefined {};

  template <class T>
    undefined special_default_value(T *);

  template <class T>
    struct has_special_default_value :
      std::integral_constant
      <
        bool,
        !std::is_same
          <
            decltype(special_default_value((T *)0)),
            undefined
          >{}
      > {};

  template <class T> struct X {
    template <class U = T, ENABLE_IF(!has_special_default_value<U>{})>
      X() : value() {}
    template <class U = T, ENABLE_IF(has_special_default_value<U>{})>
      X() : value(special_default_value((T *)0)) {}
    T value;
  };

  enum E {
    e1 = 1,
    e2 = 2
  };

  E special_default_value(E *) { return e1; }

  int main() {
    X<int> x_int;
    X<E> x_E;
    PRINT_VALUE(x_int.value);
    PRINT_VALUE(x_E.value);

    PRINT_VALUE(X<int>().value);
    PRINT_VALUE(X<E>().value);
  }

The intent is that X<int> should call the first default constructor and X<E> should call the second.

If this is intended to work, the rules for making it do so are not clear; current wording reads as if a class can have only a single default constructor, and there appears to be no mechanism for using overload resolution to choose between variants.

History
Date User Action Args
2017-02-06 00:00:00adminsetstatus: drwp -> cd4
2015-05-25 00:00:00adminsetstatus: dr -> drwp
2015-04-13 00:00:00adminsetmessages: + msg5380
2014-11-24 00:00:00adminsetstatus: tentatively ready -> dr
2014-07-07 00:00:00adminsetmessages: + msg5076
2014-07-07 00:00:00adminsetstatus: drafting -> tentatively ready
2013-05-03 00:00:00adminsetstatus: open -> drafting
2013-03-01 00:00:00admincreate