Взгляните на этот код:
struct NonConstexpr {
NonConstexpr() { }
};
template <typename T>
struct Bar {
NonConstexpr nonConstexpr;
constexpr Bar() { }
};
struct Foo {
Bar<void> bar;
constexpr Foo() { }
};
Foo имеет член Foo::bar::nonConstexpr, у которого есть конструктор, не являющийся constexpr. Итак, я ожидаю, что это не должно компилироваться. Но он компилируется с помощью gcc, clang и msvc. Это ошибка компилятора или какое-то правило позволяет компилировать этот код?
Если я добавлю элемент NonConstexpr в Foo напрямую, код больше не будет компилироваться.
(У меня возникла эта проблема, потому что я ожидал статической инициализации для глобального объекта Foo, но он был инициализирован динамически, и это вызвало проблему из-за «фиаско статического порядка инициализации»)





Is this a compiler bug, or some rule allows this code to compile?
Правило, позволяющее компилировать это:
10.1.5 The constexpr specifier [dcl.constexpr]
...
6. If the instantiated template specialization of aconstexprfunction template or member function of a class template would fail to satisfy the requirements for aconstexprfunction orconstexprconstructor, that specialization is still aconstexprfunction orconstexprconstructor, even though a call to such a function cannot appear in a constant expression. If no specialization of the template would satisfy the requirements for aconstexprfunction orconstexprconstructor when considered as a non-template function or constructor, the template is ill-formed, no diagnostic required.
Приведенная выше цитата взята из проекта стандарта CPP N4713.
Из цитаты может быть неясно, как конструктор Bar<void> может появиться в конструкторе Foo, поскольку конструктор Foo - это constexpr. Но, как отмечено в комментариях, constexpr - это не то же самое, что постоянное выражение. Конструктор Foo - это не выражение, а тем более постоянное выражение.
Позволяет компилировать, но шаблон представляет собой плохо сформированный отчет о недоставке. Интересный.
«даже если вызов такой функции не может появиться в постоянном выражении». Разве это не означает, что конструктор Bar<void> не может появиться в конструкторе Foo (конструктор Foo - constexpr, а конструктор Bar<void> - нет, поэтому он не может появиться)?
@geza - конструктор constexpr, но не постоянное выражение (это вообще не выражение).
@geza: Согласен с интерпретацией Рассказчика. Конструктор - это не выражение.
@StoryTeller: хорошо, спасибо, прочитал эту цитату несколько раз + ваш комментарий, теперь я понимаю, что происходит. Но теперь я не могу понять логику, лежащую в основе этого: «не в состоянии удовлетворить требования для конструктора ... constexpr, эта специализация по-прежнему является конструктором ... constexpr». Какие? Почему?
@geza - Честно говоря, я не уверен, почему это сформулировано именно так, вместо того, чтобы молча удалить свойство constexpr.
У вас есть рекомендация по другому моему вопросу? Как определить, будет ли Foo инициализирован статически? Если нет, возможно, я удалю эту часть своего вопроса и задам новый вопрос.
@geza: Я думаю, что ответ на другой вопрос может потребовать использования вспомогательных классов, трейтов. и т.д. Так что лучше уберите эту часть из этого вопроса и задайте новый вопрос.
К вашему сведению: stackoverflow.com/questions/53632120/…
GCC не компилирует этот код, если
Barне является шаблоном. Наверное, родственный вопрос: stackoverflow.com/questions/46119548/…