UmmaGumma
UmmaGumma

Reputation: 5693

Relation between constexpr and pure functions

Am I right, that:

And if so, why arent <cmath>'s functions defined with constexpr ?

Upvotes: 22

Views: 3554

Answers (4)

Marc Mutz - mmutz
Marc Mutz - mmutz

Reputation: 25283

Every constexpr function is pure, but not every pure function can or should be constexpr.

[Examples involving constexpr function templates are misleading, since function templates are not functions, they're patterns by which the compiler can generate functions. The outcome of function templates, their specialisations, are functions and they will be constexpr iff possible.]

A pure function is one that only depends on its arguments, or other constant state. That's pretty much what a constexpr function is. In addition, constexpr functions must be defined (not only declared) prior to their first use (recursion seems to be allowed, though), and must consist of only the return statement. That's enough to make the allowed subset Turing-complete, but the result is not necessarily the most efficient form at runtime.

Which brings us to the mathematical functions. You can probably implement constexpr sqrt() or sin(), but they would have to use a recursive implementation that the compiler can evaluate at compile-time, whereas at runtime, these would be better implemented in one assembler operation. Since constexpr uses of sqrt() and sin() are few and far apart, its better to maximise runtime performance instead, which requires a form that isn't constexprable.

You may wonder why you can't write one constexpr version of a function and one that's used at runtime, and I'd agree that would be nice to have, but the standard says you can't overload on constexprness. Maybe in C++17...

Upvotes: -1

Matthieu M.
Matthieu M.

Reputation: 299730

constexpr functions are not pure because the constexpr is a hint to the compiler that the function may be computed during the compilation if its arguments are constants and the operation mentionned in the body of the function, for these arguments, are themselves constexpr.

The latter, using template code, allows us to demonstrate an impure constexpr function:

template <typename T>
constexpr T add(T lhs, T rhs) { return lhs + rhs; }

instantiated with this type

DebugInteger operator+(DebugInteger lhs, DebugInteger rhs) {
  printf("operator+ %i %i", lhs._value, rhs._value);
  return DebugInteger(lhs._value + rhs._value);
}

Here, the operator+ is not constexpr, and may thus read/write global state.

We could say that a constexpr function is pure when evaluated at compilation time... but then it's simply replaced by a constant as far as the runtime is concerned.

Upvotes: 9

James Kanze
James Kanze

Reputation: 153889

In addition to the previous answers: constexpr on a function restricts its implementation greatly: its body must be visible to the compiler (inline), and must consist only of a single return statement. I'd be surprised if you could implement sqrt() or sin() correctly and still meet that last condition.

Upvotes: 10

James McNellis
James McNellis

Reputation: 354969

To add to what others have said, consider the following constexpr function template:

template <typename T>
constexpr T add(T x, T y) { return x + y; }

This constexpr function template is usable in a constant expression in some cases (e.g., where T is int) but not in others (e.g., where T is a class type with an operator+ overload that is not declared constexpr).

constexpr does not mean that the function is always usable in a constant expression, it means that the function may be usable in a constant expression.

(There are similar examples involving nontemplate functions.)

Upvotes: 18

Related Questions