This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 113d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-03-20


699. Must constexpr member functions be defined in the class member-specification?

Section: 9.2.6  [dcl.constexpr]     Status: CD2     Submitter: Mike Miller     Date: 26 June, 2008

N2800 comment UK 49
N2800 comment JP 12
N2800 comment DE 23

[Voted into WP at October, 2009 meeting.]

According to 9.2.6 [dcl.constexpr] paragraph 1,

The constexpr specifier shall be applied only to the definition of an object, function, or function template, or to the declaration of a static data member of a literal type (6.8 [basic.types]).

As a result, a constexpr member function cannot be simply declared in the class member-specification and defined later; it must be defined in its initial declaration.

This restriction was not part of the initial proposal but was added during the CWG review. However, the original intent is still visible in some of the wording in 9.2.6 [dcl.constexpr]. For example, paragraph 2 refers to applying the constexpr specifier to the “declaration” and not the “definition” of a function or constructor. Although that is formally correct, as definitions are also declarations, it could be confusing. Also, the example in paragraph 6 reads,

    class debug_flag {
    public:
      explicit debug_flag(bool);
      constexpr bool is_on();    // error: debug_flag not literal type
      ...

when the proximate error is that is_on is only declared, not defined. There are also many occurrences of the constexpr specifier in the library clauses where the member function is only declared, not defined.

It's not clear how much simplification is gained by this restriction. There are reasons for defining ordinary inline functions outside the class member-specification (reducing the size and complexity of the class definition, separating interface from implementation, making the editing task easier if program evolution results in an inline function being made non-inline, etc.) that would presumably apply to constexpr member functions as well. It seems feasible to allow separate declaration and definition of a constexpr function; it would simply not be permitted to use it in a constant expression before the definition is seen (although it could presumably still be used in non-constant expressions in that region, like an ordinary inline function).

If the prohibition were relaxed to allow separate declaration and definition of constexpr member functions, some questions would need to be answered, such as whether the constexpr specifier must appear on both declaration and definition (the inline specifier need not). If it can be omitted in one or the other, there's a usability issue regarding the fact that constexpr implies const; the const qualifier would need to be specified explicitly in the declaration in which constexpr was omitted.

If the current restriction is kept, the library clauses should state in an introduction that a non-defining declaration of a constexpr member function should be considered “for exposition only” and not literal code.

Notes from the September, 2008 meeting:

In addition to the original issues described above, the question has arisen whether recursive constexpr functions are or should be permitted. Although they were originally desired by the proposers of the feature, they were prohibited out of an abundance of caution. However, the wording that specified the prohibition was changed during the review process, inadvertently permitting them.

The CWG felt that there are sufficient use cases for recursion that it should not be forbidden (although a new minimum for recursion depth should be added to Annex Clause Annex B [implimits]). If mutual recursion is to be allowed, forward declaration of constexpr functions must also be permitted (answering the original question in this issue). A call to an undefined constexpr function in the body of a constexpr function should be diagnosed when the outer constexpr function is invoked in a context requiring a constant expression; in all other contexts, a call to an undefined constexpr function should be treated as a normal runtime function call, just as if it had been invoked with non-constant arguments.

Proposed resolution (July, 2009):

  1. Change Clause 7 [expr] paragraph 4 as follows:

  2. If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression appears where an integral constant expression is required (7.7 [expr.const]), in which case the program is ill-formed. [Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function. —end note]
  3. Add the indicated text to 7.7 [expr.const] paragraph 2:

  4. Change 9.2.6 [dcl.constexpr] paragraph 1 as follows:

  5. The constexpr specifier shall be applied only to the definition of an object, the declaration of a function, or function template, or to the declaration of a static data member of an effective literal type (6.8 [basic.types]). If any declaration of a function or function template has the 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 square(int x) {      // OK
        return x * x;
      }
      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 (7.7 [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]

  6. Add a new section following 16.4.6.5 [member.functions]:

  7. Implementations shall provide definitions for any non-defining declarations of constexpr functions and constructors within the associated header files.
  8. Add the following bullet to the list in Clause Annex B [implimits] paragraph 2:

(This resolution also resolves issue 695.)