Comportement étrange avec la variable membre constexpr static

Ceci est une question complémentaire à la référence non définie à static constexpr char [] [] .

Le programme suivant construit et fonctionne correctement.

#include  struct A { constexpr static char dict[] = "test"; void print() { std::cout << A::dict[0] << std::endl; } }; int main() { A a; a.print(); return 0; } 

Cependant, si je change A::print() en:

  void print() { std::cout << A::dict << std::endl; } 

Je reçois l’erreur de l’éditeur de liens suivante en g ++ 4.8.2.

 /tmp/cczmF84A.o: Dans la fonction `A :: print () ':
 socc.cc:(.text._ZN1A5printEv[_ZN1A5printEv]+0xd): référence non définie à `A :: dict '
 collect2: erreur: ld a renvoyé 1 état de sortie

L’erreur de l’éditeur de liens peut être résolue en ajoutant une ligne:

 constexpr char A::dict[]; 

en dehors de la définition de classe.

Cependant, je ne comprends pas pourquoi l’utilisation de l’un des membres du tableau ne provoque pas d’erreur de l’éditeur de liens. L’utilisation du tableau entraîne une erreur de l’éditeur de liens.

La norme ne nécessite aucun diagnostic pour une incapacité à fournir une définition lorsque celle-ci est requirejse.

3.2 Une règle de définition [basic.def.odr]

4 Chaque programme doit contenir exactement une définition de chaque fonction ou variable non inline utilisée dans ce programme; aucun diagnostic requirejs. […]

Cela signifie que les implémentations sont autorisées à optimiser les access distants à de telles variables, et c’est ce qui se passe dans votre premier cas avec GCC.

GCC et Clang ont tous deux décidé qu’ils préféraient une expérience utilisateur cohérente, dans laquelle les messages d’erreur relatifs aux définitions manquantes ne dépendaient pas du niveau d’optimisation. Cela signifie généralement que toute définition manquante provoque un message d’erreur. Cependant, dans ce cas, GCC effectue une optimisation minimale même à -O0 , évitant l’erreur.

Mais le programme est une erreur dans les deux cas, parce que même A::dict[0] est une utilisation ODR:

3.2 Une règle de définition [basic.def.odr]

3 Une variable x dont le nom apparaît sous la forme d’une expression potentiellement évaluée ex est odr-utilisée par ex sauf si l’application de la conversion lvalue en rvalue (4.1) en x donne une expression constante (5.19) qui n’invoque aucune fonction non sortingviale. et, si x est un object, ex est un élément de l’ensemble des résultats potentiels d’une expression e , où la conversion de lvalue en valeur (4.1) est appliquée à e ou e est une expression à valeur ignorée (Clause 5) […]

L’utilisation de A::dict n’implique pas la conversion de valeur d’une valeur à une autre, elle implique la conversion de tableau à pointeur, de sorte que l’exception ne s’applique pas.

En plus des informations fournies par @hvd dans sa réponse …

Du projet de norme C ++ N3337 (souligné par moi):

9.4.2 Membres de données statiques

3 Si un membre de données const static non volatile est de type intégral ou énumération, sa déclaration dans la définition de classe peut spécifier un initialisateur entre accolades ou égales dans lequel chaque clause d’initialisation qui est une expression d’affectation est une expression constante ( 5.19). Un membre de données static de type littéral peut être déclaré dans la définition de classe avec le spécificateur constexpr ; Si tel est le cas, sa déclaration doit spécifier un initialisateur de type accolade ou égal dans lequel chaque clause d’initialisation qui est une expression d’affectation est une expression constante. [ Remarque: dans les deux cas, le membre peut apparaître dans des expressions constantes. – end note ] Le membre doit toujours être défini dans une scope d’espace de noms s’il est utilisé par odr (3.2) dans le programme et la définition de la scope d’espace de noms ne doit pas contenir d’ initialiseur .

Etant donné que A::data est utilisé par odr dans l’expression A::data[0] , conformément à la norme, il doit être défini dans une scope d’espace de noms. Le fait que g ++ puisse créer un programme avec succès sans que A::data soit défini dans une étendue d’espace de noms ne corrige pas le programme. Pour être conforme aux normes, les A::data doivent être définies.