Является ли следующий код законным?
template <int N>
class foo {
public:
constexpr foo()
{
for (int i = 0; i < N; ++i) {
v_[i] = i;
}
}
private:
int v_[N];
};
constexpr foo<5> bar;
Clang принимает это, но GCC и MSVC отклоняют это.
Ошибка GCC:
main.cpp:15:18: error: 'constexpr foo<N>::foo() [with int N = 5]' called in a constant expression
15 | constexpr foo<5> bar;
| ^~~
main.cpp:4:15: note: 'constexpr foo<N>::foo() [with int N = 5]' is not usable as a 'constexpr' function because:
4 | constexpr foo()
| ^~~
main.cpp:4:15: error: member 'foo<5>::v_' must be initialized by mem-initializer in 'constexpr' constructor
main.cpp:12:9: note: declared here
12 | int v_[N];
| ^~
Если бы этот вид кода был в порядке, я мог бы сократить несколько вариантов использования index_sequence
s.
_v
должна быть инициализирована в списке инициализации до C ++ 17. Может быть, что-то изменилось в C ++ 20.int
члена никогда не будет иметь неопределенное поведение» ». Интересно, GCC не выполняет этого, или наоборот ...Ответы:
Тривиальная инициализация по умолчанию была запрещена в
constexpr
контексте до C ++ 20 .Я предполагаю, что причина в том, что легко «случайно» читать из инициализированных по умолчанию примитивов, действие, которое придает вашей программе неопределенное поведение, а выражения с неопределенным поведением запрещено
constexpr
( ref ). Язык был расширен, так что теперь компилятор должен проверить, происходит ли такое чтение, и, если это не так, должна быть принята инициализация по умолчанию. Это немного больше работы для компилятора, но (как вы видели!) Имеет существенные преимущества для программиста.Начиная с C ++ 20, законно оставлять
v_
«неинициализированным», как у вас. Затем вы перешли к присвоению значений всех его элементов, и это здорово.источник
constexpr
и просканировал связанное предложение;)