Légitime pour initialiser un tableau dans un constructeur constexpr?

11

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_sequences.

Yongwei Wu
la source
1
Gcc10 l' accepte aussi.
songyuanyao
pourriez-vous transcrire l'erreur de MSVC?
max66
... et GCC aussi.
Evg
1
@songyuanyao - g ++ 10 l'accepte en compilant C ++ 20; refuse de compiler C ++ 17 ou plus; le point semble qui _vdevrait être initialisé dans la liste d'initialisation, jusqu'à C ++ 17. Peut-être que quelque chose a changé en C ++ 20.
max66
2
@Evg C'est en fait intéressant, car cela peut suggérer que Clang utilise sa "conscience" qu'un objet de durée de stockage statique est mis à zéro pour dire "d'accord, cet objet peut avoir été initialisé par défaut mais les lectures de son intmembre n'auront jamais un comportement indéfini" ". Je me demande si GCC ne fait pas ça, ou l'inverse ...
Courses de légèreté en orbite le

Réponses:

14

L'initialisation par défaut triviale était interdite dans un constexprcontexte 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.

Cet article propose d'autoriser l'initialisation par défaut pour les types constructibles trivialement par défaut dans des contextes constexpr tout en continuant à interdire l'invocation d'un comportement non défini. En bref, tant que les valeurs non initialisées ne sont pas lues, ces états doivent être autorisés dans constexpr dans les scénarios alloués en tas et en pile.

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.

Courses de légèreté en orbite
la source
4
@ max66 Moi aussi! Tout ce que j'ai fait a été de scanner la liste des modifications C ++ 20 sur Wikipedia, de trouver quelque chose de pertinent constexpret de parcourir la proposition liée;)
Lightness Races in Orbit
3
La mauvaise partie est que cela fait plus de 20 ans que j'utilise C ++. Si chaque jour j'apprends quelque chose de nouveau ... ou je suis un mauvais programmeur ou le C ++ devient trop compliqué.
max66
5
@ max66 C'est presque certainement ce dernier. De plus, le fait qu'il change fondamentalement tous les deux ans en fait une cible en mouvement rapide. Qui peut suivre ça?! Même les compilateurs ne suivent pas cela.
Courses de légèreté en orbite le
@ max66 Ce document me vient à l'esprit: n'oubliez pas le Vasa!
Evg
@Evg Oh, wow, ce papier m'a dépassé (IRONY). Spot!
Courses de légèreté en orbite le