Relaxing constraints on constexpr functions

constexpr member functions and implicit const

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.

Allow mutation of objects whose lifetime began within the constant expression evaluation.

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.

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

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.

Together, zero-initialization and constant initialization are called static
initialization; all other initialization is dynamic initialization.
...

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-expressione is a core constant expression unless
it 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:

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,or
a 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:

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:

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 type that 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
temporary object 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 antemporary object whose lifetime
began within the evaluation of ehas not ended and is initialized with a core constant expression;

a conversion from type cvvoid * 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

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.

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.

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. Foror a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that
after 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.

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 no
other effect on the function type 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 which thata constexpr function is a member
shall be a literal type (3.9). [ Example:

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:

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.

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.5Change: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.