ISO/IEC JTC1 SC22 WG21
N3652
Richard Smith
richard@metafoo.co.uk
2013-04-18
This paper describes the subset of N3597 selected for inclusion in C++14, relaxing a number of restrictions on constexpr functions. These changes all received overwhelmingly strong or unopposed support under review of the Evolution Working Group. It also incorporates Option 2 of N3598.
The changes selected by the Evolution Working Group were:
In addition, in discussion of N3598, Option 2 was selected, which removes the rule that a constexpr non-static member function is implicitly const.
The proposed wording also resolves core issue 1361.
Change in [basic.start.init] (3.6.2)/2:
Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place. A constant initializer for an object o is an expression that is a constant expression, except that it may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types [ Note: such a class may have a non-trivial destructor ]. Constant initialization is performed:Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. ...
- if each full-expression (including implicit conversions) that appears in the initializer of a reference with static or thread storage duration is a constant expression (5.19) and the reference is bound to an lvalue designating an object with static storage duration or to a temporary (see 12.2);
- if an object with static or thread storage duration is initialized by a constructor call,
if the constructor is a constexpr constructor, if all constructor arguments are constant expressions (including conversions), and if, after function invocation substitution (7.1.5), every constructor call and full-expression in the mem-initializers and in the brace-or-equal-initializers for non-static data members is a constant expressionand if the initialization full-expression is a constant initializer for the object;- if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.
Change in [basic.types] (3.9)/10:
A type is a literal type if it is:
- void; or
- a scalar type; or
- a reference type; or
- an array of literal type; or
- a class type (Clause 9) that has all of the following properties:
- it has a trivial destructor,
every constructor call and full-expression in the brace-or-equal-initializers for non-static data members (if any) is a constant expression (5.19),- it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
- all of its non-static data members and base classes are of non-volatile literal types.
Change in [expr.const] (5.19)/2:
A conditional-expression e is a core constant expression unlessit involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function. ]the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:[ Example:
- this (5.1.1), except in a constexpr function or a constexpr constructor that is being evaluated as part of e
[ Note: when evaluating a constant expression, function invocation substitution (7.1.5) replaces each occurrence of this in a constexpr member function with a pointer to the class object. ];- an invocation of a function other than a constexpr constructor for a literal class,
ora constexpr function, or an implicit invocation of a trivial destructor (12.4) [ Note Overload resolution (13.3) is applied as usual ];- an invocation of an undefined constexpr function or an undefined constexpr constructor;
an invocation of a constexpr function with arguments that, when substituted by function invocation substitution (7.1.5), do not produce a core constant expression; [ Example:constexpr const int* addr(const int& ir) { return &ir; } // OK static const int x = 5; constexpr const int* xp = addr(x); // OK: (const int*)&(const int&)x is an // address constant expression constexpr const int* tp = addr(5); // error, initializer for constexpr variable not a constant // expression; (const int*)&(const int&)5 is not a constant // expression because it takes the address of a temporary]an invocation of a constexpr constructor with arguments that, when substituted by function invocation substitution (7.1.5), do not produce all core constant expressions for the constructor calls and full-expressions in the mem-initializer{s} (including conversions); [ Example:int x; // not constant struct A { constexpr A(bool b) : m(b?42:x) { } int m; }; constexpr int v = A(true).m; // OK: constructor call initializes // m with the value 42 after substitution constexpr int w = A(false).m; // error: initializer for m is // x, which is non-constant]an invocation of a constexpr function or a constexpr constructor that would exceed the implementation-defined recursion limits (see Annex B);- an expression that would exceed the implementation-defined limits (see Annex B);
- an operation that would have undefined behavior [ Note: including, for example, signed integer overflow (Clause 5), certain pointer arithmetic (5.7), division by zero (5.6), or certain shift operations (5.8) ];
- a lambda-expression (5.1.2);
- an lvalue-to-rvalue conversion (4.1) unless it is applied to
- a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression [ Note: a string literal (2.14.5) corresponds to an array of such objects. ], or
- a non-volatile glvalue
of literal typethat refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or- a non-volatile glvalue of literal type that refers to a non-volatile
temporaryobject whose lifetime began within the evalution of ehas not ended, initialized with a core constant expression;- an lvalue-to-rvalue conversion (4.1) or modification (5.17, 5.2.6, 5.3.2) that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
- an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
- it is initialized with a constant expression or
- it is a non-static data member of an
temporaryobject whose lifetime began within the evaluation of ehas not ended and is initialized with a core constant expression;- a conversion from type cv void * to a pointer-to-object type;
- a dynamic cast (5.2.7);
- a reinterpret_cast (5.2.10);
- a pseudo-destructor call (5.2.4);
increment or decrement operations (5.2.6, 5.3.2);- modification of an object (5.17, 5.2.6, 5.3.2) unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evalution of e;
- a typeid expression (5.2.8) whose operand is of a polymorphic class type;
- a new-expression (5.3.4);
- a delete-expression (5.3.5);
- a relational (5.9) or equality (5.10) operator where the result is unspecified; or
an assignment or a compound assignment (5.17); or- a throw-expression (15.1).
int x; // not constant struct A { constexpr A(bool b) : m(b?42:x) { } int m; }; constexpr int v = A(true).m; // OK: constructor call initializes // m with the value 42 constexpr int w = A(false).m; // error: initializer for m is // x, which is non-constant constexpr int f1(int k) { constexpr int x = k; // error: x is not initialized by a // constant expression because lifetime of k // began outside the initializer of x return x; } constexpr int f2(int k) { int x = k; // OK: not required to be a constant expression // because x is not constexpr return x; } constexpr int incr(int &n) { return ++n; } constexpr int g(int k) { constexpr int x = incr(k); // error: incr(k) is not a core constant // expression because lifetime of k // began outside the expression incr(k) return x; } constexpr int h(int k) { int x = incr(k); // OK: incr(k) is not required to be a core // constant expression return x; } constexpr int y = h(1); // OK: initializes y with the value 2 // h(1) is a core constant expression because // the lifetime of k begins inside h(1)]
Change in [expr.const] (5.19)/4:
A literal constant expression is a prvalue core constant expression of literal type, but not pointer type (after conversions as required by the context). For a literal constant expression of array or class type, each subobject of its value shall have been initialized by a constant expression. A reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function. An address constant expression is a prvalue core constant expression (after conversions as required by the context) of type std::nullptr_t or of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value. Collectively, literal constant expressions, reference constant expressions, and address constant expressions are called constant expressions.A constant expression is either a glvalue core constant expression whose value refers to an object with static storage duration or to a function, or a prvalue core constant expression whose value is an object where, for that object and each of its subobjects:
- each non-static data member of reference type refers to an object with static storage duration or to a function, and
- if the object or subobject is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (5.7), the address of a function, or a null pointer value.
Change example in [dcl.constexpr] (7.1.5)/1:
constexprintvoid square(int &x); // OK: declaration constexpr int bufsz = 1024; // OK: definition constexpr struct pixel { // error: pixel is a type int x; int y; constexpr pixel(int); // OK: declaration }; constexpr pixel::pixel(int a) : x(square(a)), y(square(a)x) // OK: definition { square(x); } constexpr pixel small(2); // error: square not defined, so small(2) // not constant (5.19) so constexpr not satisfied constexprintvoid square(int &x) { // OK: definitionreturnx *= x; } constexpr pixel large(4); // OK: square defined int next(constexpr int x) { // error: not for parameters return x + 1; } extern constexpr int memsz; // error: not a definition
Change in [dcl.constexpr] (7.1.5)/3:
The definition of a constexpr function shall satisfy the following constraints:
- it shall not be virtual (10.3);
- its return type shall be a literal type;
- each of its parameter types shall be a literal type;
- its function-body shall be = delete, = default, or a compound-statement that does not contain
s only
- null statements,
- static_assert-declarations
- typedef declarations and alias-declarations that do not define classes or enumerations,
- using-declarations,
- using-directives,
- and exactly one return statement.
- an asm-definition,
- a goto statement,
- a try-block, or
- a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
[ Example:
constexpr int square(int x) { return x * x; } // OK constexpr long long_max() { return 2147483647; } // OKconstexpr int abs(int x) { return x < 0 ? -x : x; } // OK constexpr void f(int x) // error: return type is void { /* ... */ }constexpr int abs(int x) { if (x < 0) x = -x; return x; // OK } constexpr int first(int n) { static int value = n; // error: variable has static storage duration return value; } constexpr int uninit() { int a; // error: variable is uninitialized return a; } constexpr int prev(int x) { return --x; } // OKerror: use of incrementconstexpr int g(int x, int n) { // OKerror: body not just "return expr"int r = 1; while (--n > 0) r *= x; return r; }]
Change in [dcl.constexpr] (7.1.5)/4:
The definition of a constexpr constructor shall satisfy the following constraints:
- the class shall not have any virtual base classes;
- each of the parameter types shall be a literal type;
- its function-body shall not be a function-try-block; In addition, either its function-body shall be = delete, or it shall satisfy the following constraints:
- either its function-body shall be = default, or the compound-statement of its function-body shall satisfy the constraints for a function-body of a constexpr function;
contain only
- null statements,
- static_assert-declarations
- typedef declarations and alias-declarations that do not define classes or enumerations,
- using-declarations,
- and using-directives;
- every non-variant non-static data member and base class sub-object shall be initialized (12.6.2);
- if the class is a non-empty union, or for each non-empty anonymous union member of a non-union class, exactly one non-static data member shall be initialized;
- every constructor involved in initializing non-static data members and base class sub-objects shall be a constexpr constructor
;.every assignment-expression that is an initializer-clause appearing directly or indirectly within a brace-or-equal-initializer for a non-static data member that is not named by a mem-initializer-id shall be a constant expression.[ Example:
struct Length { explicit constexpr Length(int i = 0) : val(i) { } private:int val; };]
Change in [dcl.constexpr] (7.1.5)/5:
Drafting note: these changes assume the resolution of core issue 1358 has been incorporated into the draft.
Function invocation substitution for a call of a constexpr function or of a constexpr constructor means:For a non-template, non-defaulted constexpr function
- implicitly converting each argument to the corresponding parameter type as if by copy-initialization, [ Footnote: The resulting converted value will include an lvalue-to-rvalue conversion (4.1) if the corresponding copy-initialization requires one. ]
- substituting that converted expression for each use of the corresponding parameter in the function-body,
- in a member function, substituting for each use of this (9.3.2) a prvalue pointer whose value is the address of the object for which the member function is called, and
- in a constexpr function, implicitly converting the resulting returned expression or braced-init-list to the return type of the function as if by copy-initialization. Such substitution does not change the meaning. [ Example: ... ]
, if no function argument values exist such that the function invocation substitution would produce a constant expression (5.19), the program is ill-formed; no diagnostic required. Foror a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such thatafter function invocation substitution, every constructor call and full-expression in the mem-initializers would be aan invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19)(including conversions), the program is ill-formed; no diagnostic required.[ Example:
constexpr int f(bool b) { return b ? throw 0 : 0; } // OK constexpr int f() { return f(true); } // ill-formed, no diagnostic required struct B { constexpr B(int x) : i(0) { } // x is unused int i; }; int global; struct D : B { constexpr D() : B(global) { } // ill-formed, no diagnostic required // lvalue-to-rvalue conversion on non-constant global };]
Change in [dcl.constexpr] (7.1.5)/6:
Drafting note: these changes assume the resolution of core issue 1358 has been incorporated into the draft.
If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression.[ Note: If the function is a member function it will still be const as described below. — end note ]If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed; no diagnostic required.
Change in [dcl.constexpr] (7.1.5)/8:
A constexpr specifier for a non-static member function that is not a constructor declares that member function to be const (9.3.1). [ Note:The constexpr specifier has noothereffect on thefunctiontype of a constexpr function or a constexpr constructor.— end note ] The keyword const is ignored if it appears in the cv-qualifier-seq of the function declarator of the declaration of such a member function.The class of whichthata constexpr function is a member shall be a literal type (3.9). [ Example:class debug_flag { public: explicit debug_flag(bool); constexpr bool is_on() const; // error: debug_flag not // literal type private: bool flag; }; constexpr int bar(int x, int y) // OK { return x + y + x*y; } // ... int bar(int x, int y) // error: redefinition of bar { return x * 2 + 3 * y; }]
Change in [dcl.constexpr] (7.1.5)/9:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19). Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression. [ Note: Each implicit conversion used in converting the initializer expressions and each constructor call used for the initializationshall be one of those allowed in a constant expression (5.19).is part of such a full-expression ] [ Example:struct pixel { int x, y; }; constexpr pixel ur = { 1294, 1024 }; // OK constexpr pixel origin; // error: initializer missing]
Change in [class.copy] 12.8/26:
A copy/move assignment operator that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) (e.g., when it is selected by overload resolution to assign to an object of its class type) or when it is explicitly defaulted after its first declaration. The implicitly-defined copy/move assignment operator is constexpr if
- X is a literal type, and
- the assignment operator selected to copy/move each direct base class is a constexpr function, and
- for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that member is a constexpr function.
Add new item to Clause B:
Full-expressions evaluated within a core constant expression [1,048,576].
Add new subclause after C.2 to Clause C:
C++ and ISO C++ 2011
This subclause lists the differences between C++ and ISO C++ 2011 (ISO/IEC 14882:2011, Programming Languages -- C++), by the chapters of this document.
Add a new subclause to the newly-added subclause:
Clause 7: declarations
7.1.5
Change: constexpr non-static member functions are not implicitly const member functions.
Rationale: Necessary to allow constexpr member functions to mutate the object.
Effect on original feature: Valid C++ 2011 may fail to compile in this International Standard. For example, the following code is valid in C++ 2011 but invalid in this International Standard because it declares the same member function twice with different return types:struct S { constexpr const int &f(); int &f(); };