Le code suivant est-il légitime?
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 l'accepte, mais GCC et MSVC le rejettent.
L'erreur de GCC est:
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];
| ^~
Si ce type de code était OK, je pourrais couper pas mal d'utilisations de index_sequence
s.
_v
devrait être initialisé dans la liste d'initialisation, jusqu'à C ++ 17. Peut-être que quelque chose a changé en C ++ 20.int
membre n'auront jamais un comportement indéfini" ". Je me demande si GCC ne fait pas ça, ou l'inverse ...Réponses:
L'initialisation par défaut triviale était interdite dans un
constexpr
contexte jusqu'au C ++ 20 .La raison, je suppose, est qu'il est facile de lire "accidentellement" à partir de primitives initialisées par défaut, un acte qui donne à votre programme un comportement indéfini, et les expressions avec un comportement indéfini sont carrément interdites d'être
constexpr
( réf ). Le langage a cependant été étendu de sorte que maintenant un compilateur doit vérifier si une telle lecture a lieu et, si ce n'est pas le cas, l'initialisation par défaut doit être acceptée. C'est un peu plus de travail pour le compilateur, mais (comme vous l'avez vu!) Présente des avantages substantiels pour le programmeur.Depuis C ++ 20, il est légal de laisser
v_
"non initialisé" comme vous l'avez fait. Ensuite, vous avez ensuite assigné toutes les valeurs de ses éléments, ce qui est génial.la source
constexpr
et de parcourir la proposition liée;)