Why is this constexpr
static
member function, identified by the //! Nah
comment, not seen as constexpr
when called?
From memory, member function bodies are evaluated only once the class has been completely defined.
static constexpr int bah = static_n_items();
forms part of the class definition, but it's referring to a (static) member function, which cannot yet be defined.
Solution:
defer constant expressions to a base class and derive from it.
e.g.:
struct Item_id_base
{
enum Enum
{
size, position, attributes, window_rect, max_window_size, _
};
static constexpr int n_items_ = _; // OK
constexpr auto member_n_items() const -> int { return _; } // OK
static constexpr auto static_n_items() -> int { return _; } // OK
static constexpr int so_far = n_items_; // OK
};
struct Item_id : Item_id_base
{
#ifndef OUT_OF_CLASS
static constexpr int bah = static_n_items(); // now OK
#endif
};
constexpr auto n_ids() -> int { return Item_id().member_n_items(); } // OK
auto main() -> int
{
#ifdef OUT_OF_CLASS
static constexpr int bah = Item_id::static_n_items(); // OK
#endif
}
Why do you think the standard disallows it?
Because this is illegal:
struct Item_id
{
// ... etc.
#ifndef OUT_OF_CLASS
static constexpr int bah;// = static_n_items(); //! Nah.
#endif
};
constexpr int Item_id::bah = static_n_items();
And a constexpr must have a constexpr definition. The only place we can define it is during its declaration...
... so by deduction it cannot refer to any function who's body is not yet defined.
I am at a loss to know where to look in the standard for all that. Probably 5 different, seemingly unrelated clauses :)
[class.mem]/2
Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and default member initializers (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
In the initializer of a static
data member of a class, the class is incomplete. The initializer can only see the declarations of members which precede it, and any member functions it can see are considered declared but not defined. A call to a function that is declared but not defined can not be a constant expression.