As far as I can tell the function below is not constexpr, but the code compiles in clang and g++. What am I missing?

后端 未结 2 1559
盖世英雄少女心
盖世英雄少女心 2021-02-14 12:40

I got this example from §5.19/2 in N4140:

constexpr int incr(int &n) {
    return ++n;
}

As far as I can tell, this is not a constexp

2条回答
  •  后悔当初
    2021-02-14 13:08

    In C++14 the rules for constexpr function were relaxed and the paper N3597: Relaxing constraints on constexpr functions. The paper goes into the rationale and the effects and it includes the following (emphasis mine):

    As in C++11, the constexpr keyword is used to mark functions which the implementation is required to evaluate during translation, if they are used from a context where a constant expression is required. Any valid C++ code is permitted in constexpr functions, including the creation and modification of local variables, and almost all statements, with the restriction that it must be possible for a constexpr function to be used from within a constant expression. A constant expression may still have side-effects which are local to the evaluation and its result.

    and:

    A handful of syntactic restrictions on constexpr functions are retained:

    • asm-declarations are not permitted.
    • try-blocks and function-try-blocks are not permitted.
    • Declarations of variables with static and thread storage duration have some restrictions (see below).

    and we can find this covered in N4140 section 7.1.5 [dcl.constexpr] which says:

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

    • it shall not be virtual (10.3);

    • its return type shall be a literal type;

    • each of its parameter types shall be a literal type;

    • its function-body shall be = delete, = default, or a compound-statement that does not contain

      • an asm-definition,

      • a goto statement,

      • a try-block, or

      • a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.

    The last example shows how incr can be used in a constexpr:

    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)
    

    and the rule that covers the lifetime of k begins inside h(1) is:

    • 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 evaluation of e;

    The wording in 7.1.5 [dcl.constexpr] shows us why incr is a valid constexpr:

    For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.

    As the modified example given by T.C.:

    constexpr int& as_lvalue(int&& i){ return i; }
    
    constexpr int x = incr(as_lvalue(1)) ;
    

    shows, we can indeed use incr as a subexpression of a core constant expression and therefore it is not ill-formed.

提交回复
热议问题