Je veux avoir un static const
char
tableau dans ma classe. GCC s'est plaint et m'a dit que je devrais utiliser constexpr
, bien que maintenant il me dise que c'est une référence indéfinie. Si je fais du tableau un non-membre, il se compile. Que se passe-t-il?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
c++
c++11
static-members
constexpr
Pubby
la source
la source
int
@MooingDuck. Il fonctionne bien en tant que non-membre. Cela ne violerait-il pas la règle aussi?int
triche. En tant que non-membre, cela ne devrait pas être autorisé, à moins que les règles ne soient modifiées pour C ++ 11 (possible)Réponses:
Ajoutez à votre fichier cpp:
constexpr char foo::baz[];
Raison: vous devez fournir la définition du membre statique ainsi que la déclaration. La déclaration et l'initialiseur vont dans la définition de classe, mais la définition de membre doit être séparée.
la source
decltype(foo::baz) constexpr foo::baz;
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 casconstexpr
impliqueinline
pour les variables membres statiques. La proposition dit:et modifié [basic.def] p2:
et ajoutez [depr.static_constexpr] :
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 ++ 119.4.2
[class.static.data] qui dit ( je souligne pour l'avenir ):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:Il en résulte
baz
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 section4.1
[conv.lval] qui dit: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.
la source
constexpr
n'a absolument rien à voir avec cela? (baz
est une expression constante de toute façon)integral or enumeration type
mais sinon, oui, ce qui compte, c'est que ce soit une expression constante .C'est vraiment une faille dans C ++ 11 - comme d'autres l'ont expliqué, dans C ++ 11, une variable membre statique constexpr, contrairement à tout autre type de variable globale constexpr, a un lien externe et doit donc être explicitement définie quelque part.
Il est également intéressant de noter que vous pouvez souvent vous en sortir avec des variables membres constexpr statiques sans définitions lors de la compilation avec optimisation, car elles peuvent se retrouver en ligne dans toutes les utilisations, mais si vous compilez sans optimisation, votre programme échouera souvent à se lier. Cela en fait un piège caché très courant - votre programme se compile correctement avec l'optimisation, mais dès que vous désactivez l'optimisation (peut-être pour le débogage), il ne parvient pas à se lier.
Bonne nouvelle cependant - cette faille est corrigée dans C ++ 17! L'approche est cependant un peu compliquée: en C ++ 17, les variables membres statiques constexpr sont implicitement en ligne . L' application en ligne aux variables est un nouveau concept en C ++ 17, mais cela signifie effectivement qu'elles n'ont pas besoin d'une définition explicite nulle part.
la source
La solution la plus élégante n'est-elle pas de changer le
char[]
en:static constexpr char * baz = "quz";
De cette façon, nous pouvons avoir la définition / déclaration / initialiseur en 1 ligne de code.
la source
char[]
vous pouvez utilisersizeof
pour obtenir la longueur de la chaîne au moment de la compilation, avecchar *
vous ne pouvez pas (cela retournera la largeur du type de pointeur, 1 dans ce cas).sizeof
problème, et peut être utilisée dans des solutions "en-tête uniquement"Ma solution de contournement pour le lien externe des membres statiques est d'utiliser
constexpr
des getters de membre de référence (ce qui ne rencontre pas le problème @gnzlbg soulevé en tant que commentaire à la réponse de @deddebme).Cet idiome est important pour moi car je déteste avoir plusieurs fichiers .cpp dans mes projets, et j'essaie de limiter le nombre à un, qui ne se compose que de
#include
s et d'unemain()
fonction.// foo.hpp struct foo { static constexpr auto& baz() { return "quz"; } }; // some.cpp auto sz = sizeof(foo::baz()); // sz == 4 auto& foo_baz = foo::baz(); // note auto& not auto auto sz2 = sizeof(foo_baz); // 4 auto name = typeid(foo_baz).name(); // something like 'char const[4]'
la source
Dans mon environnement, gcc vesion est 5.4.0. L'ajout de "-O2" peut corriger cette erreur de compilation. Il semble que gcc puisse gérer ce cas lorsqu'il demande une optimisation.
la source