Why is this constexpr
static
member function, identified by the //! Nah
comment, not seen as constexpr
when called?
struct Item_id
{
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
#ifndef OUT_OF_CLASS
static constexpr int bah = static_n_items(); //! Nah.
#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
}
MinGW g++ 5.1 reports
constexpr.cpp:12:46: error: 'static constexpr int Item_id::static_n_items()' called in a constant expression static constexpr int bah = static_n_items(); //! Nah.
Visual C++ 2015 reports
constexpr.cpp(12): error C2131: expression did not evaluate to a constant constexpr.cpp(12): note: failure was caused by call of undefined function or one not declared 'constexpr' constexpr.cpp(12): note: see usage of 'Item_id::static_n_items'
My text editor insists that the name in the call is the same as the name in the function definition.
It appears to have something to do with incomplete class, because with OUT_OF_CLASS
defined it compiles nicely.
But then why does the n_items_
data work, and, why such a rule (doesn't make sense to me)?
A call to a constexpr function produces the same result as a call to an equivalent non- constexpr function , except that a call to a constexpr function can appear in a constant expression. The main function cannot be declared with the constexpr specifier.
The easiest way to check whether a function (e.g., foo ) is constexpr is to assign its return value to a constexpr as below: constexpr auto i = foo(); if the returned value is not constexpr compilation will fail.
const can only be used with non-static member functions whereas constexpr can be used with member and non-member functions, even with constructors but with condition that argument and return type must be of literal types.
A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With