8

For example the code below doesn't compile unless incr() is declared constexpr:

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

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

Looking at §7.1.5/3 in C++14 we have:

The definition of a constexpr function shall satisfy the following constraints:
(3.1) — it shall not be virtual (10.3);
(3.2) — its return type shall be a literal type;
(3.3) — each of its parameter types shall be a literal type;
(3.4) — its function-body shall be = delete, = default, or a compound-statement that does not contain

(3.4.1) — an asm-definition,
(3.4.2) — a goto statement,
(3.4.3) — a try-block, or
(3.4.4) — a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.

Ayrosa
  • 3,385
  • 1
  • 18
  • 29
  • Probably worth noting the [previous question](http://stackoverflow.com/q/34211688/1708801) which covers a lot of the same ground, although from a different perspective. – Shafik Yaghmour Dec 14 '15 at 18:09
  • @ShafikYaghmour I really would appreciate to have your comments about this new question. – Ayrosa Dec 14 '15 at 18:11
  • note that the stuff you have quoted are *necessary* but not *sufficient* conditions, i.e. if those conditions are violated then the code is ill-formed, however if they are not violated then the code may or may not be correct and we have to look to other parts of the specification. – M.M Dec 16 '15 at 00:05

3 Answers3

10

Two paragraphs later, in [dcl.constexpr]/5:

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.20), or, for a constructor, a constant initializer for some object (3.6.2), the program is ill-formed; no diagnostic required.

No argument exists such that foo() could be a core constant expression because of incr(), therefore the program is ill-formed (NDR).

Barry
  • 286,269
  • 29
  • 621
  • 977
  • I don't think §7.1.5/5 has nothing to do with the question. For example, where in the Standard, does it say that the body of a constexpr function must be a core constant expression? – Ayrosa Dec 14 '15 at 17:28
  • @Ayrosa: Right there in that quote. "an invocation of the function or constructor could be an evaluated subexpression of a core constant expression" But it's not the entire body that must be, only the evaluated portions. – Ben Voigt Dec 14 '15 at 17:30
  • @BenVoigt But still, it doesn't say that the body of a constexpr function is a core constant expression. – Ayrosa Dec 14 '15 at 17:34
  • 3
    @Ayrosa: Because it's not. It's not even "an expression", it is a sequence of statements. But there has to exist one path through that body that doesn't involve evaluation of any expressions that aren't core constant expressions. – Ben Voigt Dec 14 '15 at 17:36
  • @BenVoigt I'm convinced §7.1.5/5 has nothing to do with the question. Bear in mind that the paragraph contains the following sentence (emphasis is mine): ` ... an **invocation of the function** or constructor could be an evaluated subexpression of a core constant expression`, that is, an ivocation of the function **foo**. The sentence doesn't say anything about the definition of foo. – Ayrosa Dec 14 '15 at 17:43
  • 1
    @Ayrosa **5.19** "A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions: ... an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor". No subexpressions mentioned here. – n. m. could be an AI Dec 14 '15 at 17:50
  • @Ayrosa How to you propose to evaluate an invocation of a function without considering its definition? – Barry Dec 14 '15 at 17:53
  • 1
    @Barry I think I finally understood what you're saying, and it seems to be correct. But to be more precise I would say: "No argument exists such that `foo()` could be a subexpression of **any** core constant expression, because of `incr()`, i.e., because of §5.19/2 (2.2), therefore the program is ill-formed.". Great answer (+1). – Ayrosa Dec 14 '15 at 19:02
  • 2
    "therefore the program is ill-formed" NDR. The NDR part is kinda important. – T.C. Dec 14 '15 at 20:50
8

What you're looking for is § 5.19:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

This applies to the evaluation of an expression that is a constexpr function call. That is, calling a constexpr function will be a 'core constant expression' if evaluating the function, i.e. executing the body of the function according to the rules of the C++ abstract machine, doesn't do any of the things forbidden in the list given in § 5.19.

One of items in the list is:

  • an invocation of a function other than [...] a constexpr function

So to apply this to your example: evaluating the expression foo() evaluates a call to a function incr() which is not a constexpr function, which means that the expression foo() is not a core constant expression.

Further, since the above is true for all possible invocations of your function foo, the rule in § 7.1.5/5 kicks in and means that your example program is ill-formed, no diagnostic required, even if you never actually call foo().


As Ben Voigt points out a constexpr function can contain calls to non-consexpr functions, so long as the particular evaluation of the function does not actually evaluate any such function call (or it appears in a context that does not require a constant expression).

The restrictions in 5.19 only pertain to what expressions actually end up being evaluated as part of the evaluation of an expression.

For example:

#include <iostream>

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

enum E {be_constexpr, not_constexpr};

constexpr int foo(E e = be_constexpr) {
  int n = 0;
  if (e == not_constexpr) { incr(n); }
  return n;
}

int main() {
  constexpr int a = foo(); // foo() is a constant expression
  int b = foo(not_constexpr); // may or may not evaluate `foo(non_constexpr)` at runtime. In practice modern C++ compilers will do compile-time evaluation here even though they aren't required to.
  // constexpr int c = foo(not_constexpr); // Compile error because foo(not_constexpr) is not formally a constant expression, even though modern compilers can evaluate it at compile-time.

  std::cout << a << ' ' << b << '\n';
}
bames53
  • 86,085
  • 15
  • 179
  • 244
  • It took me sometime to really appreciate the quality of both answers, yours and Ben Voigt's (+1). – Ayrosa Dec 28 '15 at 18:20
4

It doesn't.

The following is allowed, even though it does exactly what you surmise is forbidden:

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

constexpr int foo(bool x) {
    int n = 0;
    if (x) incr(n);
    return n;
}

The code in your question is disallowed by the rule with Barry quoted in his answer. But in my variation, there does exist a set of parameters (specifically, false) with which invocation of foo results in a compile-time constant expression.

Note that a diagnostic isn't required -- a conforming compiler could allow your version to compile as well.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    It is worth noting that g++ fails to compile both versions, giving the same (incorrect) error message in both cases. clang++ compiles this one and rejects the original one, citing the correct reason: "constexpr function never produces a constant expression". – n. m. could be an AI Dec 14 '15 at 18:00
  • 1
    @n.m. That bug is fixed in gcc trunk – bames53 Dec 15 '15 at 22:33
  • It took me sometime to really appreciate the quality of both answers, yours and bames53's (+1). – Ayrosa Dec 28 '15 at 18:21