Frage

Ich habe eine Template-Klasse in einer Header-Datei wie folgt definiert. Hier habe ich eine statische Variable definiert auch:

#ifndef TEST1_H_
#define TEST1_H_

void f1();

static int count;

template <class T>
class MyClass
{
public:

    void f()
    {
        ++count;
    }


};

#endif

Und ich habe main () Funktion in einer anderen CPP-Datei wie folgt definiert:

int main(int argc, char* argv[])
{
    MyClass<int> a;
    a.f();
    f1();

    cout<<"Main:" << count << "\n";

    return 0;
}

Ich habe Funktion f1 () implementiert in einer anderen CPP-Datei wie folgt aus:

void f1()
{
    MyClass<int> a;
    a.f();

    cout<<"F1: " <<count <<"\n";
}

Wenn ich diese mit VC6 kompiliert, bekam ich die Ausgabe als "F1: 0 Main: 2". Wie ist das möglich? Auch im Allgemeinen wie soll ich umgehen, wenn ich statische Variablen zusammen mit Vorlagen?

verwenden möchten
War es hilfreich?

Lösung

Sie sind immer zwei Kopien der gleichen Variablen, weil Sie eine statische Variable in einer Header-Datei deklariert haben. Wenn Sie eine globale Variable static diese Art und Weise zu erklären, sind Sie sagen, dass es zu der Übersetzungseinheit lokalen ist (die .o-Datei). Da Sie den Header in zwei Übersetzungseinheiten enthalten, erhalten Sie zwei Kopien von count.

Ich denke, was Sie wirklich hier wollen, ist ein statisches Template-Membervariable mit jedem verbunden Instanz der Template-Klasse. Es würde wie folgt aussehen:

template <class T>
class MyClass
{
    // static member declaration
    static int count;
    ...
};

// static member definition
template<class T> int MyClass<T>::count = 0;

Dies wird Ihnen eine Zählung für jede Instanziierung der Vorlage erhalten. Das heißt, Sie werden eine Zählung für MyClass<int> haben, MyClass<foo>, MyClass<bar> usw. f1() würde nun wie folgt aussehen:

void f1() {
    MyClass<int> a;
    a.f();

    cout<<"F1: " << MyClass<int>::count <<"\n";
}

Wenn Sie eine Zählung für wollen alle Instanzierungen MyClass (unabhängig von ihrer Template-Parameter), brauchen Sie eine globale Variable verwenden .

Allerdings werden Sie wahrscheinlich eine globale Variable nicht direkt, weil Sie die Gefahr der Verwendung es laufen, bevor es initialisiert wird. Sie können, indem sie eine globale statische Methode dieses Problem umgehen, die einen Verweis auf Ihre Anzahl zurückgibt:

int& my_count() {
    static int count = 0;
    return count;
}

Dann ist es aus Ihrer Klasse wie folgt erreichbar:

void f() {
    ++my_count();
}

Dadurch wird sichergestellt, dass Zählung initialisiert wird, bevor es verwendet wird, unabhängig davon, welche Kompilierungseinheit Sie es aus zugreifen. Sehen Sie sich die C ++ FAQ auf der statischen Initialisierungsreihenfolge für weitere Details .

Andere Tipps

Setzen Sie die statische Deklaration in einer Header-Datei bewirkt, dass jede CPP-Datei eine eigene Version der Variablen erhalten. So sind die beiden cout-Anweisungen Drucken verschiedener Variablen.

Haben Sie mit "F1: 1 Haupt: 1"? Sie instanziiert MyClass<int> in zwei getrennten Übersetzungseinheiten (das heißt zwei Objektdateien) und der Linker sah, dass es eine doppelte Vorlage Instanziierung war, so dass er verworfen die Instanziierung, die in f1 Objektdatei war.

Gibst du /OPT:ICF oder /OPT:REF zum VC6 Linker? Das könnte auf die doppelte Vorlage Instanziierung Entfernung in Beziehung gesetzt werden (oder nicht; Vorlage instantiations duplizieren könnte ein Sonderfall, im Vergleich zu gewöhnlichen doppelten Funktionen). GCC scheint auf einigen Plattformen etwas ähnliches zu tun.

konsistent über Compiler

Wie auch immer, ich würde nicht auf dieses Verhalten verlassen. Auch auf der Linker-Befehlszeile, die Reihenfolge der Objektdateien zu ändern könnte Auswirkungen auf die Instanziierung verworfen wird.

Es gibt eine andere Lösung ist, können Sie eine gemeinsame übergeordnete Klasse erstellen und setzen diese statische Variable drin, dann Template-Klasse es privat machen erbt, hier ein Beispiel:

class Parent
{
protected: 
    static long count;
};

long Parent::count = 0;

template<typename T>
class TemplateClass: private Parent
{
private: 
    int mKey;
public:
    TemplateClass():mKey(count++){}
    long getKey(){return mKey;}
}

int main()
{
    TemplateClass<int> obj1;
    TemplateClass<double> obj2;

    std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl;
    std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl;

    return 0;
}

Ausgabe wird sein:

Object 1 key is: 0 
Object 2 key is: 1

Ich denke, das ist eigentlich nicht definiertes Verhalten .

Nach C ++ 14 [basic.def.odr] / 6:

  

kann es mehr als eine Definition eines [...] Elementfunktion einer Klasse-Vorlage [...] in einem Programm vorgesehen, dass jede Definition in einer anderen Übersetzungseinheit angezeigt wird, und vorausgesetzt, dass die Definitionen die folgenden Anforderungen erfüllen . Bei einer solchen benannte Einheit D definiert in mehr als einer Übersetzungseinheit, dann

     
      
  • jede Definition von D setzt sich aus der gleichen Folge von Token bestehen; und
  •   
  • in jeder Definition von D, Namen entspricht, nachgeschlagen nach 3,4, wird auf eine Entität bezieht innerhalb der Definition von D definiert ist, oder ist auf die gleiche Einheit, nach Überlast Auflösung (13.3) und nach dem Abgleich von Teilvorlage beziehen Spezialisierung (14.8.3), mit der Ausnahme, dass ein Name auf einen nichtflüchtigen beziehen   const Objekt mit interner oder keiner Bindung, wenn das Objekt den gleichen Literal-Typen in allen Definitionen von D hat, und das Objekt wird mit einem konstanten Ausdruck (5.19) initialisiert wird, und das Objekt ist nicht ODR-verwendet wird, und das Objekt den gleichen Wert hat in allen Definitionen von D; [...]
  •   

Das Problem ist, dass in der ersten .cpp Datei, der Name count innerhalb f1 auf ein anderes Objekt verweist, als der Namen count innerhalb f1 in der zweiten .cpp Datei, so dass die Bedingung zu verletzen, dass entsprechende Namen zu derselben Einheit beziehen.

Sie sind verschiedene Objekte wegen des static Spezifizierer, die besagt, dass jede Übersetzungseinheit mit diesem Namen ein eigenes Objekt bekommt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top