Ayrosa
Ayrosa

Reputation: 3513

Where in C++14 Standard does it say that a non-constexpr function cannot be used in a definition of a constexpr function?

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.

Upvotes: 8

Views: 713

Answers (3)

bames53
bames53

Reputation: 88215

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

Upvotes: 8

Barry
Barry

Reputation: 303537

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).

Upvotes: 10

Ben Voigt
Ben Voigt

Reputation: 283793

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.

Upvotes: 4

Related Questions