Почему эта статическая функция-член constexpr не вызывается как constexpr при вызове?
Почему это constexpr
static
функция-член, определяемая //! Nah
комментарий, не рассматривается как constexpr
когда звонили?
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
constexpr.cpp:12:46: ошибка: 'static constexpr int Item_id::static_n_items()' вызывается в константном выражении static constexpr int bah = static_n_items(); //! Неа.
Отчеты по Visual C++ 2015
constexpr.cpp(12): ошибка C2131: выражение не было константой constexpr.cpp(12): примечание: сбой был вызван вызовом неопределенной функции или одной не объявленной constexpr constexpr.cpp(12): примечание: см. использование Item_id:: static_n_items
Мой текстовый редактор настаивает на том, что имя в вызове совпадает с именем в определении функции.
Похоже, что-то делать с неполным классом, потому что с OUT_OF_CLASS
определил, что он хорошо компилируется.
Но тогда почему n_items_
данные работают, и, почему такое правило (не имеет смысла для меня)?
2 ответа
По памяти тела функций-членов оцениваются только после того, как класс полностью определен.
static constexpr int bah = static_n_items();
является частью определения класса, но он ссылается на (статическую) функцию-член, которая пока не может быть определена.
Решение:
отложить константные выражения до базового класса и получить его.
например:
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
}
Как вы думаете, почему стандарт запрещает это?
Потому что это незаконно:
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();
И constexpr должен иметь определение constexpr. Единственное место, которое мы можем определить это во время его объявления...
... так что дедукция не может относиться к какой-либо функции, чье тело еще не определено.
Я в недоумении, чтобы узнать, где искать в стандарте для всего этого. Вероятно, 5 разных, казалось бы, не связанных статей:)
[Class.mem]/2
В спецификации членов класса класс рассматривается как завершенный в теле функций, аргументах по умолчанию, спецификациях исключений и инициализаторах членов по умолчанию (включая такие вещи во вложенных классах). В противном случае он рассматривается как неполный в своей собственной спецификации членов класса.
В инициализаторе static
член данных класса, класс неполный. Инициализатор может видеть только объявления членов, которые предшествуют ему, и любые функции-члены, которые он видит, считаются объявленными, но не определены. Вызов функции, которая объявлена, но не определена, не может быть константным выражением.