5 Expressions [expr]

5.20 Constant expressions [expr.const]

Certain contexts require expressions that satisfy additional requirements as detailed in this subclause; other contexts have different semantics depending on whether or not an expression satisfies these requirements. Expressions that satisfy these requirements, assuming that copy elision is performed, are called constant expressions. [ Note: Constant expressions can be evaluated during translation. — end note ]

constant-expression:
    conditional-expression

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following expressions:

  • this ([expr.prim.this]), except in a constexpr function or a constexpr constructor that is being evaluated as part of e;

  • an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor ([class.dtor]) [ Note: Overload resolution ([over.match]) is applied as usual  — end note ];

  • an invocation of an undefined constexpr function or an undefined constexpr constructor;

  • an invocation of an instantiated constexpr function or constexpr constructor that fails to satisfy the requirements for a constexpr function or constexpr constructor ([dcl.constexpr]);

  • an expression that would exceed the implementation-defined limits (see Annex [implimits]);

  • an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard [ Note: including, for example, signed integer overflow (Clause [expr]), certain pointer arithmetic ([expr.add]), division by zero ([expr.mul]), or certain shift operations ([expr.shift])  — end note ];

  • an lvalue-to-rvalue conversion ([conv.lval]) unless it is applied to

    • a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or

    • a non-volatile glvalue that refers to a subobject of a string literal ([lex.string]), or

    • a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or

    • a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

  • an lvalue-to-rvalue conversion ([conv.lval]) that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;

  • an assignment expression ([expr.ass]) or invocation of an assignment operator ([class.copy]) that would change the active member of a union;

  • 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

    • its lifetime began within the evaluation of e;

  • in a lambda-expression, a reference to this or to a variable with automatic storage duration defined outside that lambda-expression, where the reference would be an odr-use ([basic.def.odr], [expr.prim.lambda]); [ Example:

    void g() {
      const int n = 0;
      [=] {
        constexpr int i = n;   // OK, n is not odr-used and not captured here
        constexpr int j = *&n; // ill-formed, &n would be an odr-use of n
      };
    }
    

     — end example ] [ Note: If the odr-use occurs in an invocation of a function call operator of a closure type, it no longer refers to this or to an enclosing automatic variable due to the transformation ([expr.prim.lambda]) of the id-expression into an access of the corresponding data member. [ Example:

    auto monad = [](auto v) { return [=] { return v; }; };
    auto bind = [](auto m) {
      return [=](auto fvm) { return fvm(m()); };
    };
    
    // OK to have captures to automatic objects created during constant expression evaluation.
    static_assert(bind(monad(2))(monad)() == monad(2)());
    

     — end example ]  — end note ]

  • a conversion from type cv void * to a pointer-to-object type;

  • a dynamic cast ([expr.dynamic.cast]);

  • a reinterpret_cast ([expr.reinterpret.cast]);

  • a pseudo-destructor call ([expr.pseudo]);

  • modification of an object ([expr.ass], [expr.post.incr], [expr.pre.incr]) unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

  • a typeid expression ([expr.typeid]) whose operand is a glvalue of a polymorphic class type;

  • a new-expression ([expr.new]);

  • a delete-expression ([expr.delete]);

  • a relational ([expr.rel]) or equality ([expr.eq]) operator where the result is unspecified; or

  • a throw-expression ([expr.throw]).

If e satisfies the constraints of a core constant expression, but evaluation of e would evaluate an operation that has undefined behavior as specified in Clauses [library] through [thread] of this International Standard, it is unspecified whether e is a core constant expression.

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

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)

 — end example ]

An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression. [ Note: Such expressions may be used as bit-field lengths ([class.bit]), as enumerator initializers if the underlying type is not fixed ([dcl.enum]), and as alignments ([dcl.align]).  — end note ]

A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only

and where the reference binding (if any) binds directly. [ Note: Such expressions may be used in new expressions ([expr.new]), as case expressions ([stmt.switch]), as enumerator initializers if the underlying type is fixed ([dcl.enum]), as array bounds ([dcl.array]), and as non-type template arguments ([temp.arg]).  — end note ] A contextually converted constant expression of type bool is an expression, contextually converted to bool (Clause[conv]), where the converted expression is a constant expression and the conversion sequence contains only the conversions above.

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:

  • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,

  • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a function, or a null pointer value, and

  • if the value is an object of class or array type, each subobject satisfies these constraints for the value.

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

Note: Since this International Standard imposes no restrictions on the accuracy of floating-point operations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression (or the same operations on the same values) during program execution.89Example:

bool f() {
    char array[1 + int(1 + 0.2 - 0.1 - 0.1)];  // Must be evaluated during translation
    int size = 1 + int(1 + 0.2 - 0.1 - 0.1);   // May be evaluated at runtime
    return sizeof(array) == size;
}

It is unspecified whether the value of f() will be true or false.  — end example ]  — end note ]

If an expression of literal class type is used in a context where an integral constant expression is required, then that expression is contextually implicitly converted (Clause [conv]) to an integral or unscoped enumeration type and the selected conversion function shall be constexpr. [ Example:

struct A { 
  constexpr A(int i) : val(i) { } 
  constexpr operator int() const { return val; }
  constexpr operator long() const { return 43; }
private: 
  int val; 
}; 
template<int> struct X { }; 
constexpr A a = 42; 
X<a> x;             // OK: unique conversion to int
int ary[a];         // error: ambiguous conversion 

 — end example ]

Nonetheless, implementations are encouraged to provide consistent results, irrespective of whether the evaluation was performed during translation and/or during program execution.