This is a fair point. But surely the counter-argument is that very few things in C++ work this way today (constexpr, const, and noexcept being the three exceptions I can think of, and all three of them famously leading to tons of boilerplate).
The usual way to handle "documented invariants" or "requirements" of this nature is to annotate them in the source code with either comments or static_asserts:
class MyFoo {
int one() const; // I promise this will never stop being callable on a const object
constexpr int two(); // I promise this will never stop being callable in a constexpr context
int three() noexcept; // I promise this will never stop being nothrow
int four();
};
static_assert(is_trivially_copy_constructible_v<MyFoo>); // I promise this will never stop being trivially copy-constructible
static_assert(is_aggregate_v<MyFoo>); // I promise this will never stop being memberwise initializable
static_assert(sizeof(&MyFoo::four)); // I promise this function will never be overloaded
static_assert(is_same_v<decltype(declval<MyFoo&>().four(), int>); // I promise this return type will never change
and so on and so forth. If we had the syntax for it, then we could easily annotate our source code with
static_assert(is_constexpr_constructible_v<MyFoo>); // I promise this will never stop being constexpr constructible
static_assert(constexpr(MyFoo().four())); // I promise this expression will never stop being evaluable in a constexpr context
Const and noexcept are actually special cases in two related ways: they participate in name-mangling and need to match up across module boundaries. You can declare a function "noexcept" without ever seeing its definition. But this is not true for constexpr; a function with no visible definition cannot possibly be evaluated at compile time!
(A) The constexpr-ness of a function is part of its API.
(B) If a function is not inline, then it cannot be constexpr.
(C) If a function is inline, then its definition is part of its API.
(QED?): We can let the constexpr-ness of a function be deduced from its definition, without changing what is part of its API.
So yeah, I think that if your client code relies on the constexpr-ness of a function that isn't specifically annotated as such, your code is just as broken as client code that relies on the trivial-ness or nothrow-ness of a function that isn't specifically annotated as such. But does that mean that we need special language keywords for annotating constexpr-ness, trivial-ness, or nothrow-ness? IMHO perhaps not.
my $.02,
Arthur