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 1561
盖世英雄少女心
盖世英雄少女心 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 12:58

    As far as I can tell, this is not a constexpr function.

    Why do you say that? The example from §5.19/2 reads:

    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;
    }
    

    incr(k) not being a core constant expression does not mean incr cannot not be a constexpr function.

    Under C++14's constexpr rules, it is possible to use incr in a constexpr context, for example:

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

    Unless it's downright impossible for the body of the function to be constexpr (for example, calling a non-constexpr function unconditionally), the compiler has no reason to produce an error at the point of definition.

    A constexpr function may even contain paths/branches in the body which would not be constexpr. As long as they are never taken in a constexpr context, you will not get an error. For example:

    constexpr int maybe_constexpr(bool choice, const int& a, const int& b) {
        return choice ? a : b;
    }
    
    constexpr int a = 0;
    int b = 1;
    static_assert(maybe_constexpr(true, a, b) == 0, "!");
    

    live example

    0 讨论(0)
  • 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.

    0 讨论(0)
提交回复
热议问题