Relaxing constraints on constexpr functions (original) (raw)

ISO/IEC JTC1 SC22 WG21
N3652
Richard Smith
richard@metafoo.co.uk
2013-04-18

constexpr member functions and implicit const

This paper describes the subset of N3597selected for inclusion in C++14, relaxing a number of restrictions on constexprfunctions. These changes all received overwhelmingly strong or unopposed support under review of the Evolution Working Group. It also incorporates Option 2 ofN3598.

Accepted Changes

The changes selected by the Evolution Working Group were:

In addition, in discussion of N3598, Option 2 was selected, which removes the rule that aconstexpr non-static member function is implicitly const.

The proposed wording also resolves core issue 1361.

Proposed wording

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 constexprconstructors 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:

Change in [basic.types] (3.9)/10:

A type is a literal type if it is:

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:

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:

Change example in [dcl.constexpr] (7.1.5)/1:

constexpr intvoid 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

constexpr intvoid square(int &x) { // OK: definition return x *= 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:

Change in [dcl.constexpr] (7.1.5)/4:

The definition of a constexpr constructor shall satisfy the following constraints:

[ 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: * 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: ... ] For a non-template, non-defaulted constexpr function , 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. For or 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 a an invocation of the function or constructor could be an evaluated subexpression of a coreconstant 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 orconstexpr 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 aconstexpr 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 noother effect on the function type of a constexpr function or a constexpr constructor. — end note ] The keywordconst 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 which that a 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 initialization shall 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

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(); };

Acknowledgements

Thanks to Jens Maurer for assistance in preparing this wording, and to Bjarne Stroustrup and Gabriel Dos Reis for guidance and encouragement.