22

Am I right, that:

  • Any function defined with constexpr is a pure function, and
  • Any pure function can be and must be defined with constexpr if it's not very expensive for compiler.

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

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
UmmaGumma
  • 5,633
  • 1
  • 31
  • 45
  • Even with the relaxed `constexpr` requirements, `` functions can't be `constexpr` because they modify the global variable `errno` – Ryan Haining Oct 10 '14 at 04:15

4 Answers4

18

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

James McNellis
  • 348,265
  • 75
  • 913
  • 977
10

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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Wow, I hadn't know about it. Thanks and +1 :) – UmmaGumma Mar 28 '11 at 17:39
  • 4
    I presume things like this are valid: "constexpr int fact(int n) { return (n != 0 ? fact(n - 1) * n : 1); }" so whilst it might be tricky to implement sqrt() and sin() in terms of constexpr, I think it is certainly possible. – Clinton Mar 29 '11 at 01:11
  • @Clinton 7 You have a point: recursion is allowed (intentionally, or by oversight?). So an implementation should be possible, as long as the recursion is bound by the input argument. (You couldn't, for example, recurse until the difference between the two approximations was less than a given value.) But it certainly wouldn't look like the usual sqrt or sin implementation. – James Kanze Mar 29 '11 at 09:05
  • 7
    @James: Principally, you can express every loop with recursion, and state can be "conserved" using function arguments, so the set of `constexpr` functions is turing complete. See also http://en.wikipedia.org/wiki/Church-Turing_thesis . As a maybe interesting note, have a look at http://gitorious.org/metatrace , a complete compile time ray tracer, which among other things also has square root computation and ray/sphere and ray/plane intersection computation. sin/cos haven't been harmed, though. – Sebastian Mach Sep 01 '11 at 10:51
9

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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 2
    A template function is really a family of functions, and in your example, any member of the family that is impure is also, necessarily, not constexpr. – HighCommander4 Feb 08 '12 at 23:16
  • 1
    @HighCommander4: the function *is* marked with `constexpr` (the keyword), it just does not end up being a constant expression for some cases. I would avoid conflating *constant expression* (the term of art) with `constexpr` (the C++ keyword). My point was that `constexpr` on templates was valid even though some instanciations may not end up being usable in *constant expressions*. – Matthieu M. Feb 09 '12 at 07:35
  • 1
    @MatthieuM.: I disagree. You have written a template and requested `constexpr` on it. Upon specialisation, the compiler then honors that request, iff it can. You cannot say that the function is marked with constexpr, because a function template is not a function. That'd be like saying that `std::forward()` takes its argument by rvalue reference, whereas the sole purpose of `std::forward()` is to take the argument by either rvalue or lvalue reference, depending on the template argument. – Marc Mutz - mmutz Jun 15 '12 at 15:05
  • @MarcMutz-mmutz: (Not 100% certain, but…) I think the correct term is instantiation, not specialisation. – Marcelo Cantos Mar 24 '13 at 20:18
-1

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

Marc Mutz - mmutz
  • 24,485
  • 12
  • 80
  • 90
  • 4
    Not every `constexpr` function is pure. It's permissible for a constexpr function to do anything at all, so long as there exists a set of arguments to it which cause a pure execution. – Richard Smith Aug 07 '12 at 19:41
  • @RichardSmith: since the definition of a pure function is that it only depends on constants and its arguments, and doesn't cause visible side effects, I don't see how changing the arguments can change pureness of a function. Care to elaborate? – Marc Mutz - mmutz Aug 08 '12 at 07:01
  • 3
    @mmutz Consider `int n; constexpr int f(bool b) { return b ? 0 : (n *= getc()); }`. This is legal, because `f` produces a constant expression when given the argument `true`. But `f` is obviously not a pure function. – Richard Smith Aug 15 '12 at 02:43
  • @Richard if the argument is false, that function call is not a constant expression. – Aykhan Hagverdili Nov 19 '21 at 15:09
  • 1
    @Ayxan I agree, but that's not relevant to the question. `f` is a `constexpr` function and is not pure, therefore not all `constexpr` functions are pure. – Richard Smith Nov 20 '21 at 18:36