C ++ 17 introduit des variables en ligne
C ++ 17 corrige ce problème pour constexpr static
les variables membres nécessitant une définition hors ligne si elle était utilisée par odr. Voir la seconde moitié de cette réponse pour les détails pré-C ++ 17.
La proposition P0386 Variables en ligne introduit la possibilité d'appliquer le inline
spécificateur aux variables. En particulier, ce cas constexpr
implique inline
pour les variables membres statiques. La proposition dit:
Le spécificateur en ligne peut être appliqué aux variables ainsi qu'aux fonctions. Une variable déclarée en ligne a la même sémantique qu'une fonction déclarée en ligne: elle peut être définie, de manière identique, en plusieurs unités de traduction, doit être définie dans chaque unité de traduction dans laquelle elle est utilisée, et le comportement du programme est comme si il y a exactement une variable.
et modifié [basic.def] p2:
Une déclaration est une définition à moins que
...
- il déclare un membre de données statique en dehors d'une définition de classe et la variable a été définie dans la classe avec le spécificateur constexpr (cette utilisation est obsolète; voir [depr.static_constexpr]),
...
et ajoutez [depr.static_constexpr] :
Pour la compatibilité avec les normes internationales C ++ antérieures, un membre de données statique constexpr peut être redondant de manière redondante en dehors de la classe sans initialiseur. Cette utilisation est obsolète. [ Exemple:
struct A {
static constexpr int n = 5;
};
constexpr int A::n;
- fin d'exemple]
C ++ 14 et versions antérieures
En C ++ 03, nous étions uniquement autorisés à fournir des initialiseurs en classe pour les intégrales const ou les types d'énumération const , en C ++ 11, l'utilisation de constexpr
cela a été étendue aux types littéraux .
En C ++ 11, nous n'avons pas besoin de fournir une définition de portée d'espace de noms pour un constexpr
membre statique s'il n'est pas utilisé par odr , nous pouvons le voir dans le projet de section standard de C ++ 11 9.4.2
[class.static.data] qui dit ( je souligne pour l'avenir ):
[...] Un membre de données statique 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 initialiseur d'accolade ou d'égalité dans lequel chaque clause d'initialisation qui est une expression d'affectation est une expression constante. [Remarque: dans ces deux cas, le membre peut apparaître dans des expressions constantes. —End note]
Le membre doit toujours être défini dans une portée d'espace de noms s'il est utilisé par odr (3.2) dans le programme et la définition de portée d'espace de noms ne doit pas contenir d'initialiseur.
Alors la question devient, est baz
utilisée ici:
std::string str(baz);
et la réponse est oui , et nous avons donc également besoin d'une définition de la portée de l'espace de noms.
Alors, comment déterminer si une variable est utilisée par odr ? Le libellé original de C ++ 11 dans la section 3.2
[basic.def.odr] dit:
Une expression est potentiellement évaluée à moins qu'il ne s'agisse d'un opérande non évalué (article 5) ou d'une sous-expression de celui-ci. Une variable dont le nom apparaît comme une expression potentiellement évaluée est utilisée par odr à moins
qu'il ne s'agisse d'un objet qui satisfait aux exigences pour apparaître dans une expression constante (5.19) et que la conversion lvalue-en-rvalue (4.1) est immédiatement appliquée .
Il en résultebaz
une expression constante, mais la conversion de lvaleur en rvalue n'est pas immédiatement appliquée car elle n'est pas applicable car il s'agit d' baz
un tableau. Ceci est couvert dans la section 4.1
[conv.lval] qui dit:
Une glvalue (3.10) d'un type T sans fonction et sans tableau peut être convertie en une valeur pr.53 [...]
Ce qui est appliqué dans la conversion tableau-pointeur .
Ce libellé de [basic.def.odr] a été modifié en raison du rapport d'anomalie 712 car certains cas n'étaient pas couverts par ce libellé, mais ces changements ne modifient pas les résultats pour ce cas.