2

I have a question regarding following comment about conditional expressions used in constexpr functions:

A branch of a conditional expression that is not taken in a constexpr function is not evaluated. Source: conditional evaluation of functions

As already written in the source you can have constexpr functions like

constexpr int check(int i) {
    return (0<=i && i<10) ? i : throw out_of_range();
}

and only the branch that is taken is evaluated. So far, so good. But why is this invalid in combination with templates. So let's take this basic example here:

template <int N>
constexpr int times(int y) {
    return (N<0) ? 0 : y+times<N-1>(y);
}

times<5>(10);

The compilation fails because the template instantiation depth exceeds the maximum even though the false branch of the conditional is only taken 4 times. Then it should take the true branch and return 0. Of course it can be rewritten using enable_if or whatever but I only would like to know following things:

  • Is this statement not valid when it comes to subexpression type evaluation?

  • Why does this fail even though the statement above claims that the subexpression is not evaluated? I guess the types have to be evaluated anyway (e.g., to check if the requirement that both branches of the conditional are having the same type is fulfilled) and therefore it ends in an infinite template instantiation. Correct assumption?

  • Is there a place in the c++ standard that describes this behavior?

Community
  • 1
  • 1

1 Answers1

2

You're misunderstanding what it means to evaluate something. Evaluation is something that happens at execution time (even if that execution happens while the compiler is running).

Template instantiation is a static property of your code. If you write times<N-1> you are asking to instantiate that template. It doesn't matter if you call that function or not; you write the instantiation, so it gets instantiated.

This is why recursive metaprogramming usually handles the terminal case through a template specialization.

This is why if constexpr was added to C++17. Because it has the power to, not merely conditionally evaluate statements, but conditionally discard statements, making the other branch effectively not exist. Which is not something that ever existed before. This allows the other branch to contain code that would have been statically il-formed otherwise.

So this would work:

if constexpr(N < 0) return 0 else return y+times<N-1>(y);

The second clause would be discarded and thus not instantiated.

So the statement is correct; subexpressions are conditionally evaluated. You're just misinterpreting how it applies to your case.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982