ISO/IEC JTC1 SC22 WG21
N3598
Richard Smith
2013-03-12

constexpr member functions and implicit const

Problem

In C++11, constexpr member functions are implicitly const. This creates problems for literal class types which desire to be usable both within constant expressions and outside them:

struct A {
  constexpr A() : n(3) {}
  constexpr int getN() const /*implicit*/ { return n; }
  int n;
};
struct B {
  constexpr B() : a() {}
  constexpr const A &getA() const /*implicit*/ { return a; }
  A &getA() { return a; }            // cannot make this 'constexpr'
  A a;
};
constexpr int n = B().getA().getN(); // error, selected overload for
                                     // B::getA() is not constexpr.

This problem only exists for the implicit this parameter to a function; constexpr functions can have parameters of (non-const) T * and T &.

Analysis

Several alternatives have been suggested to resolve this problem:

If we select the second option, the backwards-compatibility issue can be made less severe by encouraging implementors to diagnose constexpr but not explicitly const member functions in their C++11 modes.

Proposed wording

Option 2

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

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.

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 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; }
  
end example ]

Option 3

Strawman syntax: allow mutable in the location where const would be written: constexpr A &getA() mutable { return a; }

Change the grammar for parameters-and-qualifiers in [dcl.decl] (8)/4:

parameters-and-qualifiers:

( parameter-declaration-clause ) mutableopt cv-qualifier-seqopt ref-qualifieropt exception-specificationopt attribute-specifier-seqopt

Change in [dcl.fct] (8.3.5)/1:

In a declaration T D where D has the form

D1 ( parameter-declaration-clause ) mutableopt cv-qualifier-seqopt ref-qualifieropt exception-specificationopt attribute-specifier-seqopt [...]

Change in [dcl.fct] (8.3.5)/2:

In a declaration T D where D has the form

D1 ( parameter-declaration-clause ) mutableopt cv-qualifier-seqopt ref-qualifieropt exception-specificationopt attribute-specifier-seqopt trailing-return-type [...]

Add a new paragraph after [dcl.fct] (8.3.5)/6:

If mutable is part of a function declarator, const shall not be present in the cv-qualifier-seq and that declarator shall be used to declare a non-static member function. [ Note: Such a function is not const even if it is declared with the constexpr specifier. — end note ]

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

A constexpr specifier for a non-static member function that is not a constructor and is not declared with the mutable keyword declares that member function to be const (9.3.1). [ 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 (3.9). [ Example: [...] — end example ]