87 votes

Référence non définie à static const int

J'ai couru dans un problème intéressant aujourd'hui. Considérons cet exemple simple:

template <typename T>
void foo(const T & a) { /* code */ }

// This would also fail
// void foo(const int & a) { /* code */ }

class Bar
{
public:
   static const int kConst = 1;
   void func()
   {
      foo(kConst);           // This is the important line
   }
};

int main()
{
   Bar b;
   b.func();
}

Lors de la compilation j'obtiens une erreur:

Undefined reference to 'Bar::kConst'

Maintenant, je suis assez sûr que c'est parce que l' static const int n'est pas définie n'importe où, ce qui est intentionnel, car, selon ma compréhension, le compilateur doit être en mesure de faire le remplacement au moment de la compilation et pas besoin d'une définition. Toutefois, étant donné que la fonction prend un const int & paramètre, il semble ne pas faire de la substitution, et préférant une référence. Je peux résoudre ce problème en faisant les changements suivants:

foo(static_cast<int>(kConst));

Je crois que c'est maintenant forcer le compilateur à faire un temporaire de type int, et puis passer une référence à ce qui peut réussir à faire au moment de la compilation.

Je me demandais si c'était intentionnel, ou suis-je en attendre trop de gcc pour être en mesure de gérer ce cas? Ou est-ce quelque chose que je ne devrais pas être en train de faire pour une raison quelconque?

66voto

Steve Jessop Points 166970

C'est intentionnel, 9.4.2/4 dit:

Si une donnée membre statique est de const intégrale ou const type d'énumération, sa déclaration dans la classe définition pouvez spécifier un constante d'initialiseur qui doit être une partie intégrante d'expression constante (5.19) Dans ce cas, le membre peut apparaître dans partie intégrante des expressions constantes. L' membre doit encore être définie dans un espace de noms de la portée si elle est utilisée dans la programme

Lorsque vous passez à la donnée membre statique par référence const, vous "utilisation", 3.2/2:

Une expression est potentiellement évalué à moins qu'il apparaît à l'endroit où une partie intégrante expression constante est requise (voir 5.19), est l'opérande de l'opérateur sizeof (5.3.3), ou est l'opérande de l'opérateur typeid et l'expression ne désigne pas une lvalue de polymorphes type de classe (5.2.8). Un objet ou non surchargé la fonction est utilisé si son nom apparaît dans un potentiellement expression évaluée.

Donc, en fait, vous "utilisez" quand vous passez par une valeur trop, ou dans un static_cast. C'est juste que GCC a vous laisser descendre le crochet dans un cas mais pas dans l'autre.

[Edit: gcc est l'application des règles de C++0x brouillons: "Une variable ou non surchargé la fonction dont le nom apparaît comme potentiellement évalué l'expression est de rll-utilisé, sauf si c'est un objet qui satisfait aux exigences pour apparaître dans une expression constante (5.19) et la lvalue-à-rvalue de conversion (4.1) est immédiatement appliquée.". La statique de la fonte effectue lvalue-rvalue conversion immédiatement, donc dans C++0x ce n'est pas "utilisé".]

Le problème pratique de la const de référence est qu' foo est en droit de prendre l'adresse de son argument, et de la comparer par exemple avec l'adresse de l'argument à partir d'un autre appel, stockée dans un mondial. Depuis une donnée membre statique est un objet unique, cela signifie que si vous appelez foo(kConst) à partir de deux différents Cus, l'adresse de l'objet passé doit être la même dans chaque cas. Autant que je sache, GCC ne peut pas faire en sorte que, sauf si l'objet est défini dans un (et un seul) TU.

OK, donc dans ce cas - foo est un modèle, d'où la définition est visible dans tous les Syndicats, alors peut-être que le compilateur pourrait, en théorie, d'écarter le risque qu'il fait n'importe quoi avec l'adresse. Mais en général, vous avez certainement ne devriez pas prendre des adresses ou des références à des non-existant objets ;-)

31voto

pelya Points 886

Si vous écrivez une variable const statique avec un initialiseur à l'intérieur de la déclaration de classe, c'est comme si vous aviez écrit

 class Bar
{
      enum { kConst = 1 };
}
 

et GCC le traitera de la même manière, ce qui signifie qu’il n’a pas d’adresse.

Le code correct devrait être

 class Bar
{
      static const int kConst;
}
const int Bar::kConst = 1;
 

2voto

TonyK Points 8604

g ++ version 4.3.4 accepte ce code (voir ce lien ). Mais g ++ version 4.4.0 le rejette.

1voto

quamrana Points 6411

Je pense que cet artefact de C ++ signifie que chaque fois que l'on fait référence à Bar::kConst , sa valeur littérale est utilisée à la place.

Cela signifie qu'en pratique, il n'y a pas de variable pour faire un point de référence.

Vous devrez peut-être faire ceci:

 void func()
{
  int k = kConst;
  foo(k);
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X