Stack Exchange
constexpr pow() function compiler fail
If I try something like:
template <auto X, auto E>
constexpr decltype(X) const_pow() noexcept
{
  return !E ? 1 : 1 == E ? X : (E % 2 ? X : 1) * const_pow<X * X, E / 2>();
}

I get an error:
prog.cc: In instantiation of 'constexpr decltype (X) const_pow() [with auto X = 100000000; auto E = 0; decltype (X) = int]':
prog.cc:6:73:   recursively required from 'constexpr decltype (X) const_pow() [with auto X = 100; auto E = 1; decltype (X) = int]'
prog.cc:6:73:   required from 'constexpr decltype (X) const_pow() [with auto X = 10; auto E = 3; decltype (X) = int]'
prog.cc:11:32:   required from here
prog.cc:6:62: warning: integer overflow in expression of type 'int' results in '1874919424' [-Woverflow]
    6 |   return !E ? 1 : 1 == E ? X : (E % 2 ? X : 1) * const_pow<X * X, E / 2>();
      |                                                            ~~^~~
prog.cc:6:73: error: no matching function for call to 'const_pow<(100000000 * 100000000), (0 / 2)>()'

The workaround is straightforward (e.g. requires, or if constexpr), but why does the compiler not end recursion by itself?

Because both branches are instanted regardless of the value of the condition.

I'll add a tag, language-lawyer. Yes, I know both are instantiated, but why.

Because that's how C++ works. If you want discarded branches, use if constexpr. "Normal" if or conditional operator means "check the condition at run time". The actual check might be optimized away at code generation stage, but that doesn't affect syntactic/semantic analysis.

I'm hoping someone will find the time to copy&paste the relevant sections of the Standard.

The relevant sections of the standard is the entire standard. There is no passage anywhere that says anything like "but templates are not instantiated in not-taken branches of ternary operators when the first operand is a constant expression", but you need to read it all to make sure there are indeed no such words.

@n.m. yes, this is what I was hoping for, a language-lawyer who knows the Standard by heart and can recite it as if it were the Bible.

There is no need. A modicum of common sense is enough. if constexpr was added to the language for a reason.

"[stmt.if]/2 If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool (7.7); this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity (13.1), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated."

Then you indeed need to scan the whole standard in order to convince yourself that a) no other context creates a discarded statement, and b) a discarded statement is the only context in which a template specialization is not instantiated. No other way to prove a negative.

[temp.inst]/5:

Unless a function template specialization is a declared specialization, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program.

[basic.def.odr]/11:

Every program shall contain at least one definition of every function or variable that is odr-used in that program outside of a discarded statement; no diagnostic required.

[basic.def.odr]/8:

A function is odr-used if it is named by a potentially-evaluated expression or conversion.

[basic.def.odr]/4.1:

A function is named by an expression or conversion if it is the selected member of an overload set in an overload resolution performed as part of forming that expression or conversion [...]

The call in the conditional expression odr-uses const_pow<...> regardless of whether the branch is taken or not, causing its instantiation.
