constexpr
means "can be used where a constant expression is required". The "implied const" for declared objects doesn't mean we can't have non-const objects in other contexts. For instance, a somewhat contrived example, created from your own:
template<int>
struct foo {
};
struct A {
int i = 0;
constexpr A() {}
constexpr int a() { return i; }
constexpr int b() const {return 12; }
constexpr A& c() { ++i; return *this; }
};
int main()
{
foo<A{}.c().a()> f1;
}
Obviously the template argument must be a constant expression. Now, A{}
is a prvalue of a literal type with a constexpr c'tor, and it's a non-const object. The member function is allowed to modify this "constant" because the entire computation can collapse to a constant expression at compile time. That's the rationale for the rules, on one foot.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…