Это ошибка? Конструктор Constexpr бесшумно становится неконференциальным
Посмотрите на этот код:
struct NonConstexpr {
NonConstexpr() { }
};
template <typename T>
struct Bar {
NonConstexpr nonConstexpr;
constexpr Bar() { }
};
struct Foo {
Bar<void> bar;
constexpr Foo() { }
};
Foo
имеет член, Foo::bar::nonConstexpr
, который имеет конструктор non-constexpr. Итак, я ожидаю, что это не должно компилироваться. Но он компилируется с помощью gcc, clang и msvc. Является ли это ошибкой компилятора, или какое-то правило позволяет этот код компилироваться?
Если я добавлю член NonConstexpr
в Foo
напрямую, код больше не компилируется.
(У меня возникла эта проблема, потому что я ожидал статической инициализации для глобального объекта Foo
, но он получил динамическую инициализацию и вызвал проблему из-за "статического фиаско порядка инициализации")
Ответы
Ответ 1
Является ли это ошибкой компилятора, или какое-то правило позволяет этот код компилироваться?
Правило, которое позволяет компилировать это:
10.1.5 Спецификация constexpr [dcl.constexpr]
...
6. Если экземпляр шаблон специализация constexpr
шаблон функции или член функции шаблона класса будет не в состоянии удовлетворить требования, предъявляемые к constexpr
функции или constexpr
конструктора, что специализация по - прежнему constexpr
функция или constexpr
конструктор, хотя вызов таких функция не может появляться в постоянном выражении. Если никакая специализация шаблона не удовлетворяет требованиям для функции constexpr
или конструктора constexpr
при рассмотрении как не-шаблонной функции или конструктора, шаблон плохо сформирован, не требуется диагностика.
Вышеприведенная цитата взята из стандартного черновика CP47 N4713.
Из цитаты не может быть ясно, как Bar<void>
Конструктор может появиться в Foo
конструктору Foo
конструктор constexpr
. Но, как отмечено в комментариях, constexpr
не совпадает с постоянным выражением. Конструктор Foo
не является выражением, а тем более постоянным выражением.
Ответ 2
Я ожидал статической инициализации для глобального объекта Foo
Вероятно, вы забыли указать конструктор constexpr
для этого глобального объекта Foo
, чтобы компилятор не требовал, чтобы конструктор был constexpr
.
Если вы укажете constexpr
, который не будет компилироваться.
могу ли я каким-то образом (время компиляции) определить, что Foo :: Foo() является constexpr или нет? Поэтому я могу поставить static_assert для него.
Вы можете:
Поскольку оператор noexcept
всегда возвращает true
для константного выражения, его можно использовать для проверки того, принимает ли конкретный вызов функции constexpr
постоянного выражения.
static_assert(noexcept(Foo{}), "Foo{} is not constexpr.");