Constexpr constructor fails to satisfy the requirements, but still constexpr. Why?

  • A+
Category:Languages

The standard says about template constexpr functions/constructors in dcl.constexpr/6:

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression. If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed, no diagnostic required.

The interesting part is:

fail to satisfy the requirements for a ... constexpr constructor, that specialization is still a ... constexpr constructor

So, even if a constructor is marked with constexpr, it may not be used in a constant expression.

Why does this rule exist? Why isn't constexpr removed, when a function doesn't satisfy the requirements?

The current behavior is bad in two ways:

  • the non-constexpr-ness isn't caught at the closest possible location, but at the actual constexpr expression, where it is used. So we have to find the offending part, where constexpr silently removed.
  • an object, which is intended to be statically initialized (because it has a constexpr constructor), will be dynamically initialized without any errors/warnings (because the constructor isn't "really" constexpr).

Does this rule have some pros, which balances the cons of it?

 


This rule allows you to write a templated constructor/function and mark it as constexpr even when it's not always constexpr (only at least sometimes).

For example, std::pair has constexpr constructors, but it is of course usable outside of constant expressions.

This is quite sensible, because otherwise you would have to duplicate all these functions (once with constexpr and once without), even if the code is exactly the same. Let's not even consider ambiguity.

Since it is generally impossible to prove that a template cannot ever satisfy constexpr, no diagnostic is required for it (but it's ill-formed so compilers can complain to you if they can prove this for a given case).

You are correct that this is not very useful if you want to specify "this function shall only be usable in constant expression", but that's not what this wording is aiming for.

Edit: To clarify, constexpr for functions only means "legal to evaluate inside a constant expression" (more precise wording here), not "can only be evaluated at compile-time". By contrast, constexpr variables must be initialized with a constant expression.


Another edit: We have exact wording to discuss, thanks to @JackAidley!

If the instantiated template specialization of a constexpr function template would fail to satisfy the requirements for a constexpr function, the constexpr specifier is ignored and the specialization is not a constexpr function.

The problem with this is that "there is at least one set of arguments for which the function can be constant-evaluated" is part of the "requirements for a constexpr function". Therefore, compilers cannot implement this clause, since it is not possible to prove (in general) whether such a set exists for a given function (or a function template instantiation). You either have to muddy this requirement further or give up on this aspect. It seems the committee chose the latter.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: