web-dev-qa-db-fra.com

Référence indéfinie au membre de classe statique

Quelqu'un peut-il expliquer pourquoi le code suivant ne compile pas? Au moins sur g ++ 4.2.4.

Et plus intéressant encore, pourquoi il compilera quand je lance MEMBER to int?

#include <vector>

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

int main(){  
    vector<int> v;  
    v.Push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.Push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}
188
Pawel Piatkowski

En réalité, 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 vous débarrasser de la référence indéfinie.

191
Drew Hall

Le problème vient d'un conflit intéressant entre les nouvelles fonctionnalités C++ et ce que vous essayez de faire. Voyons d’abord le Push_back Signature:

void Push_back(const T&)

Il attend une référence à un objet de type T. Sous l'ancien système d'initialisation, un tel membre existe. Par exemple, le code suivant compile parfaitement:

#include <vector>

class Foo {
public:
    static const int MEMBER;
};

const int Foo::MEMBER = 1; 

int main(){
    std::vector<int> 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 objet réel quelque part dans lequel cette valeur est stockée. Si, toutefois, vous passez à la nouvelle méthode de spécification de membres const statique, comme ci-dessus, Foo::MEMBER n'est plus un objet. C'est une constante, un peu semblable à:

#define MEMBER 1

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

71
Douglas Mayle

La norme C++ requiert une définition pour votre membre const statique si la définition est nécessaire.

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

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

C'est un cas vraiment intéressant, et je pense en fait qu'il vaut la peine de soulever un problème pour que le std soit modifié afin d'avoir le même comportement pour votre membre constant!

Bien que cela puisse paraître étrange, cela pourrait être perçu comme une utilisation légitime de l'opérateur unaire '+'. Fondamentalement, le résultat de la unary + est une valeur rvalue. Les règles de liaison de rvalues ​​à des références const s'appliquent et nous n'utilisons pas l'adresse de notre membre const statique:

v.Push_back( +Foo::MEMBER );
58
Richard Corden

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;
10
iso9660

Aucune idée pourquoi le casting fonctionne, mais Foo :: MEMBER n'est alloué que lorsque Foo est chargé pour la première fois, 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.

1
Paul Tomblin

En ce qui concerne la deuxième question: Push_ref prend référence en tant que paramètre et vous ne pouvez pas avoir de référence à un membre statique d'une classe/struct. Une fois que vous appelez static_cast, une variable temporaire est créée. Et une référence à cet objet peut être transmise, tout fonctionne parfaitement.

Ou du moins mon collègue qui a résolu cela l'a dit.

0
Quarra

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

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

La partie constexpr crée un statique expression par opposition à un statique variable - et se comporte comme une définition de méthode inline extrêmement simple. L'approche s'est toutefois révélée un peu bancale avec constexprs C-string dans les classes de modèle.

0
starturtle