Constantes intégrales C ++ + opérateur de choix = problème!

J’ai récemment découvert un problème gênant dans un grand programme que je développe; Je voudrais comprendre comment le réparer de la meilleure façon. J’ai coupé le code à l’exemple minimal suivant.

#include  using std::cin; using std::cout; class MagicNumbers { public: static const int BIG = 100; static const int SMALL = 10; }; int main() { int choice; cout <> choice; int stuff = (choice < 20) ? MagicNumbers::SMALL : MagicNumbers::BIG; // PROBLEM! cout << "You got " << stuff << "\n"; return 0; } 

Je rencontre des erreurs de liens dans gcc 4.1.2 lors de la compilation avec -O0 ou -O1 mais tout va bien lors de la compilation avec -O2 ou -O3. Il relie bien en utilisant MS Visual Studio 2005 quelles que soient les options d’optimisation.

test.cpp :(. text + 0xab): référence non définie à `MagicNumbers :: SMALL ‘

test.cpp :(. text + 0xb3): référence non définie à `MagicNumbers :: BIG ‘

J’ai examiné le code d’assemblage intermédiaire et, oui, le code non optimisé considérait SMALL et BIG comme des variables int externes, tandis que le code optimisé utilisait les nombres réels. Chacune des modifications suivantes résout le problème:

  • Utilisez enum au lieu de int pour les constantes: enum {SMALL = 10}

  • Convertissez la constante (n’importe laquelle) à chaque utilisation: (int)MagicNumbers::SMALL ou (int)MagicNumbers::BIG ou même MagicNumbers::SMALL + 0

  • Utilisez une macro: #define SMALL 10

  • Ne pas utiliser l’opérateur de choix: if (choice < 20) stuff = MagicNumbers::SMALL; else stuff = MagicNumbers::BIG; if (choice < 20) stuff = MagicNumbers::SMALL; else stuff = MagicNumbers::BIG;

J’aime mieux la première option (cependant, ce n’est pas idéal car nous utilisons en fait uint32_t au lieu de int pour ces constantes, et enum est synonyme d’int.) Mais ce que je veux vraiment demander, c’est: à qui appartient cet insecte?

Suis-je responsable de ne pas comprendre le fonctionnement des constantes intégrales statiques?

Dois-je blâmer gcc et espérer un correctif (ou peut-être que la dernière version a déjà un correctif, ou peut-être existe-t-il un argument de ligne de commande obscur pour que cela fonctionne)?

En attendant, je viens de comstackr mon code avec des optimisations, et c’est très pénible de déboguer: -O3

En dépit des conseils classiques, j’ai constaté que static const int ... me donne toujours plus de maux de tête que la bonne vieille enum { BIG = 100, SMALL = 10 }; . Et avec C ++ 11 fournissant des énumérations fortement typées, j’ai encore moins de raisons d’utiliser static const int ...

C’est un problème connu . La norme est à blâmer ou à vous de ne pas avoir fourni de définition de la statique. En fonction de votre sharepoint vue 🙂

Les membres de données statiques ne fonctionnent pas comme ça en C ++:

Les membres de données statiques ne font pas partie des objects d’un type de classe donné; ce sont des objects séparés. Par conséquent, la déclaration d’un membre de données statique n’est pas considérée comme une définition. Le membre de données est déclaré dans l’étendue de la classe, mais la définition est effectuée dans l’étendue du fichier. Ces membres statiques ont un lien externe.

Vous ne déclarez que ces constantes, même si vous les initialisez. Vous devez toujours les définir à la scope de l’espace de noms:

 class MagicNumbers { public: static const int BIG = 100; static const int SMALL = 10; }; const int MagicNumbers::BIG; const int MagicNumbers::SMALL; 

Cela éliminera les erreurs de lien.

Heh, selon le standard C ++, 9.4.2 (class.static.data):

Si un membre de données statique est de type littéral const, 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. 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 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 étendue d’espace de nommage s’il est utilisé dans le programme et la définition de l’étendue d’espace de nommage ne doit pas contenir d’initialiseur.

La déclaration est donc correcte, mais vous devez toujours avoir une définition quelque part. J’ai toujours pensé que vous pourriez maîsortingser la définition, mais je suppose que ce n’est pas conforme à la norme.

Je suis nouveau en C ++, mais je pense que votre déclaration de classe déclare uniquement que ces membres statiques existent, vous devez toujours les définir quelque part:

 class MagicNumbers { public: static const int BIG; static const int SMALL; }; const int MagicNumbers::BIG = 100; const int MagicNumbers::SMALL = 10; 

Les niveaux d’optimisation les plus élevés incluent probablement un niveau d’parsing statique suffisamment approfondi pour déterminer que BIG et SMALL peuvent être échangés avec leurs valeurs réelles et ne pas leur atsortingbuer de mémoire réelle (la sémantique sera la même); définissez donc ces variables dans ce cas serait redondant, d’où le lien OK.

J’aurais du mal à affirmer que c’est le bogue de n’importe qui.

Les intégrales constantes statiques qui reçoivent des valeurs au sharepoint déclaration ne sont pas des variables, mais des expressions constantes. Pour qu’il y ait une variable, vous devez toujours la définir.

Les règles de l’opérateur ternaire sont assez complexes, probablement nécessairement, et ne disent en réalité rien sur les expressions constantes, mais seulement sur les valeurs; évidemment, le compilateur pense qu’elles devraient être des variables, à moins que l’optimisation ne soit accélérée. Je pense qu’il est libre d’interpréter l’expression dans un sens ou dans l’autre (en tant qu’expression constante ou variable).

Vous devez toujours leur allouer de l’espace quelque part:

 class MagicNumbers { public: static const int BIG = 100; static const int SMALL = 10; }; const int MagicNumbers::BIG; const int MagicNumbers::SMALL; 

Pourquoi vos nombres magiques dans une classe?

 namespace MagicNumbers { const int BIG = 100; const int SMALL = 10; } 

Problème résolu sans avoir à s’inquiéter des failles du standard C ++.