Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't constexpr implied when applicable?

These should probably be in different questions, but they're related so...

  1. Why do we need to write constexpr at all? Given a set of restrictions couldn't a compiler evaluate code to see if it satisfies the constexpr requirements, and treat it as constexpr if it does? As a purely documentation keyword I'm not sure it holds up because I can't think of a case where I (the user of someone else's constexpr function) should really care if it's run time or not.

    Here's my logic: If it's an expensive function I think as a matter of good practice I should treat it as such regardless of whether I give it compile-time constant input or not. That might mean calling it during load time and saving off the result, instead of calling it during a critical point in the execution. The reason is because constexpr doesn't actually guarantee to me that it will not be executed in run time in the first place — so perhaps a new/different mechanism should do that.

  2. The constexpr restrictions seem to exclude many, if not most, functions from being compile-time evaluated which logically could be. I've read this is at least in part (or perhaps wholly?) to prevent infinite looping and hanging the compiler. But, if this is the reason, is it legitimate?

Shouldn't a compiler be able to compute if, for any given constexpr function with the given inputs used, it loops infinitely? This is not solving the halting problem for any input. The input to a constexpr function is compile time constant and finite, so the compiler only has to check for infinite looping for a finite set of input: the input actually used. It should be a regular compilation error if you write a compile-time infinite loop.

like image 850
David Avatar asked Sep 18 '13 16:09

David


1 Answers

I asked a very similar question, Why do we need to mark functions as constexpr?

When I pressed Richard Smith, a Clang author, he explained:

The constexpr keyword does have utility.

It affects when a function template specialization is instantiated (constexpr function template specializations may need to be instantiated if they're called in unevaluated contexts; the same is not true for non-constexpr functions since a call to one can never be part of a constant expression). If we removed the meaning of the keyword, we'd have to instantiate a bunch more specializations early, just in case the call happens to be a constant expression.

It reduces compilation time, by limiting the set of function calls that implementations are required to try evaluating during translation. (This matters for contexts where implementations are required to try constant expression evaluation, but it's not an error if such evaluation fails -- in particular, the initializers of objects of static storage duration.)

This all didn't seem convincing at first, but if you work through the details, things do unravel without constexpr. A function need not be instantiated until it is ODR-used, which essentially means used at runtime. What is special about constexpr functions is that they can violate this rule and require instantiation anyway.

Function instantiation is a recursive procedure. Instantiating a function results in instantiation of the functions and classes it uses, regardless of the arguments to any particular call.

If something went wrong while instantiating this dependency tree (potentially at significant expense), it would be difficult to swallow the error. Furthermore, class template instantiation can have runtime side-effects.

Given an argument-dependent compile-time function call in a function signature, overload resolution may incur instantiation of function definitions merely auxiliary to the ones in the overload set, including the functions that don't even get called. Such instantiations may have side effects including ill-formedness and runtime behavior.

It's a corner case to be sure, but bad things can happen if you don't require people to opt-in to constexpr functions.

As for constexpr objects, certain types can produce core constant expressions which are usable in constant expression contexts without having been declared constexpr. But you don't really want the compiler to try evaluating every single expression at compile time. That's what constant propagation is for. On the other hand it seems pretty essential to document when something needs to happen at compile time.

like image 106
Potatoswatter Avatar answered Oct 13 '22 22:10

Potatoswatter