Référence indéfinie à un membre de classe statique

Quelqu’un peut-il expliquer pourquoi le code suivant ne sera pas compilé? Au moins sur g ++ 4.2.4.

Et plus intéressant, pourquoi cela va-t-il être compilé quand je transforme MEMBER en int?

#include  class Foo { public: static const int MEMBER = 1; }; int main(){ vector v; v.push_back( Foo::MEMBER ); // undefined reference to `Foo::MEMBER' v.push_back( (int) Foo::MEMBER ); // OK return 0; } 

Vous devez définir le membre statique quelque part (après la définition de la classe). Essaye ça:

 class Foo { /* ... */ }; const int Foo::MEMBER; int main() { /* ... */ } 

Cela devrait éliminer la référence indéfinie.

Le problème vient d’un conflit intéressant de nouvelles fonctionnalités C ++ et de ce que vous essayez de faire. Tout d’abord, regardons la signature push_back :

 void push_back(const T&) 

Il attend une référence à un object de type T Sous l’ancien système d’initialisation, un tel membre existe. Par exemple, le code suivant comstack très bien:

 #include  class Foo { public: static const int MEMBER; }; const int Foo::MEMBER = 1; int main(){ std::vector v; v.push_back( Foo::MEMBER ); // undefined reference to `Foo::MEMBER' v.push_back( (int) Foo::MEMBER ); // OK return 0; } 

C’est parce qu’il y a un object réel quelque part qui contient cette valeur. Si, toutefois, vous passez à la nouvelle méthode de spécification des membres const statiques, comme ci-dessus, Foo::MEMBER n’est plus un object. C’est une constante, un peu comme:

 #define MEMBER 1 

Mais sans les maux de tête d’une macro de préprocesseur (et avec le type de sécurité). Cela signifie que le vecteur, qui attend une référence, ne peut pas en obtenir un.

Le standard C ++ nécessite une définition pour votre membre statique statique si la définition est nécessaire.

La définition est requirejse, par exemple si son adresse est utilisée. push_back prend son paramètre par référence à const, et le compilateur a donc ssortingctement besoin de l’adresse de votre membre et vous devez le définir dans l’espace de noms.

Lorsque vous lancez explicitement la constante, vous créez un temporaire et c’est ce temporaire qui est lié à la référence (sous des règles spéciales dans la norme).

C’est un cas vraiment intéressant, et je pense que cela vaut la peine de soulever un problème pour que le std soit changé pour avoir le même comportement pour votre membre constant!

Bien que, de manière étrange, cela pourrait être considéré comme une utilisation légitime de l’opérateur unaire «+». Fondamentalement, le résultat de l’ unary + est une valeur et donc les règles de liaison des valeurs de référence aux const s’appliquent et nous n’utilisons pas l’adresse de notre membre const statique:

 v.push_back( +Foo::MEMBER ); 

Aaa.h

 class Aaa { protected: static Aaa *defaultAaa; }; 

Aaa.cpp

 // You must define an actual variable in your program for the static members of the classes static Aaa *Aaa::defaultAaa; 

Aucune idée de la raison du fonctionnement de la dissortingbution, mais Foo :: MEMBER n’est pas alloué avant le premier chargement de Foo, et comme vous ne le chargez jamais, il n’est jamais alloué. Si vous aviez une référence à un Foo quelque part, cela fonctionnerait probablement.

En ce qui concerne la deuxième question: push_ref prend la référence en tant que paramètre, et vous ne pouvez pas avoir de référence à un const statique memeber d’une classe / struct. Une fois que vous appelez static_cast, une variable temporaire est créée. Et une référence à cet object peut être passée, tout fonctionne très bien.

Ou du moins mon collègue qui a résolu ce problème l’a dit.

Avec C ++ 11, ce qui précède serait possible pour les types de base comme

 class Foo { public: static constexpr int MEMBER = 1; }; 

La partie constexpr crée une expression statique par opposition à une variable statique – et se comporte exactement comme une définition de méthode en ligne extrêmement simple. L’approche s’est avérée un peu bancale avec les constexprs de chaînes de caractères à l’intérieur des classes de modèles.