7 Declarations [dcl.dcl]

7.1 Specifiers [dcl.spec]

7.1.5 The constexpr specifier [dcl.constexpr]

The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type ([basic.types]). If any declaration of a function or function template has constexpr specifier, then all its declarations shall contain the constexpr specifier. [ Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier.  — end note ] [ Note: Function parameters cannot be declared constexpr. — end note ] [ Example:

constexpr int 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))  // OK: definition
  { }
constexpr pixel small(2);       // error: square not defined, so small(2)
                                // not constant ([expr.const]) so constexpr not satisfied

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

 — end example ]

A constexpr specifier used in the declaration of a function that is not a constructor declares that function to be a constexpr function. Similarly, a constexpr specifier used in a constructor declaration declares that constructor to be a constexpr constructor. constexpr functions and constexpr constructors are implicitly inline ([dcl.fct.spec]).

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

Example:

constexpr int square(int x) 
  { return x * x; }             // OK
constexpr long long_max() 
  { return 2147483647; }        // OK
constexpr int abs(int x) 
  { return x < 0 ? -x : x; }    // OK 
constexpr void f(int x)         // error: return type is void /* ... */ 
constexpr int prev(int x)
  { return --x; }               // error: use of decrement
constexpr int g(int x, int n) { // error: body not just “return expr”
  int r = 1;
  while (--n > 0) r *= x;
  return r;
}

 — end example ]

In a definition of a constexpr constructor, each of the parameter types shall be a literal type. In addition, either its function-body shall be = delete or = default or it shall satisfy the following constraints:

Example:

struct Length { 
  explicit constexpr Length(int i = 0) : val(i) { }
private: 
    int val; 
}; 

 — end example ]

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,91 substituting that converted expression for each use of the corresponding parameter in the function-body, and, for constexpr functions, 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:

constexpr int f(void *) { return 0; }
constexpr int f(...) { return 1; }
constexpr int g1() { return f(0); }         // calls f(void *)
constexpr int g2(int n) { return f(n); }    // calls f(...) even for n == 0
constexpr int g3(int n) { return f(n*0); }  // calls f(...)

namespace N {
  constexpr int c = 5;
  constexpr int h() { return c; }
}
constexpr int c = 0;
constexpr int g4() { return N::h(); }       // value is 5, c is not looked up again after the substitution

 — end example ]

For a constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression ([expr.const]), the program is ill-formed; no diagnostic required. For a 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 a constant expression (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() { throw 0; }              // 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
};

 — end example ]

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 not a constexpr function or constexpr constructor. [ 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 yield a constexpr function or constexpr constructor, the program is ill-formed; no diagnostic required.

A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that a call to a constexpr function can appear in a constant expression.

A constexpr specifier for a non-static member function that is not a constructor declares that member function to be const ([class.mfct.non-static]). [ Note: The constexpr specifier has no other effect on the function type. — 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 that function is a member shall be a literal type ([basic.types]). [ Example:

class debug_flag { 
public: 
  explicit debug_flag(bool); 
  constexpr bool is_on();       // 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; } 

 — end 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 ([expr.const]). 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. 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 ([expr.const]). [ Example:

struct pixel { 
  int x, y; 
}; 
constexpr pixel ur = { 1294, 1024 };// OK 
constexpr pixel origin;             // error: initializer missing 

 — end example ]

The resulting converted value will include an lvalue-to-rvalue conversion ([conv.lval]) if the corresponding copy-initialization requires one.