Constexpr constructors (C++11)

Note: IBM supports selected features of C++11, known as C++0x before its ratification. IBM will continue to develop and implement the features of this standard. The implementation of the language level is based on IBM's interpretation of the standard. Until IBM's implementation of all the C++11 features is complete, including the support of a new C++11 standard library, the implementation may change from release to release. IBM makes no attempt to maintain compatibility, in source, binary, or listings and other compiler interfaces, with earlier releases of IBM's implementation of the new C++11 features.

A constructor that is declared with a constexpr specifier is a constexpr constructor. Previously, only expressions of built-in types could be valid constant expressions. With constexpr constructors, objects of user-defined types can be included in valid constant expressions.

Definitions of constexpr constructors must satisfy the following requirements:
  • The containing class must not have any virtual base classes.
  • Each of the parameter types is a literal type.
  • Its function body is = delete or = default; otherwise, it must satisfy the following constraints:
    • It is not a function try block.
    • The compound statement in it must contain only the following statements:
      • null statements
      • static_assert declarations
      • typedef declarations that do not define classes or enumerations
      • using directives
      • using declarations
  • Each nonstatic data member and base class subobject is initialized.
  • Each constructor that is used for initializing nonstatic data members and base class subobjects is a constexpr constructor.
  • Initializers for all nonstatic data members that are not named by a member initializer identifier are constant expressions.
  • When initializing data members, all implicit conversions that are involved in the following context must be valid in a constant expression:
    • Calling any constructors
    • Converting any expressions to data member types

The implicitly defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no initializer and an empty compound-statement. If that user-defined default constructor would satisfy the requirements of a constexpr constructor, the implicitly defined default constructor is a constexpr constructor.

A constexpr constructor is implicitly inline.

The following examples demonstrate the usage of constexpr constructors:
struct BASE {
};

struct B2 {
  int i;
};

//NL is a non-literal type.
struct NL {     
  virtual ~NL() {   
  }
};    

int i = 11;

struct D1 : public BASE {
  //OK, the implicit default constructor of BASE is a constexpr constructor.
  constexpr D1() : BASE(), mem(55) { }
  
  //OK, the implicit copy constructor of BASE is a constexpr constructor.
  constexpr D1(const D1& d) : BASE(d), mem(55) { } 

  //OK, all reference types are literal types.
  constexpr D1(NL &n) : BASE(), mem(55) { }

  //The conversion operator is not constexpr.
  operator int() const { return 55; }      

private:    
  int mem;
};  

struct D2 : virtual BASE { 
  //error, D2 must not have virtual base class.
  constexpr D2() : BASE(), mem(55) { }    

private:
  int mem; 
};  

struct D3 : B2 {
  //error, D3 must not be a function try block.   
  constexpr D3(int) try : B2(), mem(55) { } catch(int) { }

  //error, illegal statement is in body.
  constexpr D3(char) : B2(), mem(55) { mem = 55; } 
  
  //error, initializer for mem is not a constant expression. 
  constexpr D3(double) : B2(), mem(i) { }

  //error, implicit conversion is not constexpr. 
  constexpr D3(const D1 &d) : B2(), mem(d) { }                   

  //error, parameter NL is a non-literal type.
  constexpr D3(NL) : B2(), mem(55) { } 

private: 
  int mem;
};