Czy następujący kod jest prawidłowy?
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 to akceptuje, ale GCC i MSVC to odrzucają.
Błąd GCC to:
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];
| ^~
Gdyby tego rodzaju kod był OK, mógłbym wyciąć całkiem sporo zastosowań index_sequence
s.
_v
należy zainicjować na liście inicjalizacji, aż do C ++ 17. Może coś się zmieniło w C ++ 20.int
członka nigdy nie będą miały niezdefiniowanego zachowania „. Zastanawiam się, czy GCC tego nie robi, czy odwrotnie ...Odpowiedzi:
Trywialna domyślna inicjalizacja była zabroniona w
constexpr
kontekście do C ++ 20 .Wydaje mi się, że powodem jest to, że łatwo „przypadkowo” odczytać z prymitywów zainicjowanych domyślnie, czynność, która nadaje programowi niezdefiniowane zachowanie, a wyrażeniom o nieokreślonym zachowaniu zabrania się
constexpr
( ref ). Język został jednak rozszerzony, więc teraz kompilator musi sprawdzić, czy taki odczyt ma miejsce, a jeśli nie, należy zaakceptować domyślną inicjalizację. Jest to trochę więcej pracy dla kompilatora, ale (jak widzieliście!) Ma znaczące zalety dla programisty.Od C ++ 20 pozostawienie
v_
„niezainicjowanej” tak jak ty jest legalne . Następnie przypisałeś wszystkie wartości jego elementów, co jest świetne.źródło
constexpr
i przejrzenie powiązanej propozycji;)