2010-09-13 5 views
12
#include <map> 
#include <iostream> 
template <typename T> 
class A 
{ 
static std::map<int, int> data; 
public: 
A() 
{ 
    std::cout << data.size() << std::endl; 
    data[3] = 4; 
} 
}; 

template <typename T> 
std::map<int, int> A<T>::data; 

//std::map<int, int> A<char>::data; 

A<char> a; 

int main() 
{ 
return 0; 
} 

Was ist falsch daran? Ohne explizite Instanziierung bricht es um C++ - Vorlage statische Element Instanziierung

 data[3] = 4;
. Explizite Instanziierung löst das Problem, aber das Programm bricht nach
std::cout << data.size() << std::endl;
ab, was bedeutet, dass der statische Klassenschablonenmember data instanziiert wurde.

+1

Welcher Compiler? Ich glaube nicht, dass das deine Schuld ist. – Potatoswatter

+0

Dies wird mit VS2010 berechnet. – linuxuser27

+0

Ich benutze vs2008 und es kompiliert in der Tat, aber das Programm bricht bei Zeilendaten [3] = 4 – mrs

Antwort

2

Ich habe Visual C++ nicht handlich, aber ich sehe das gleiche Problem mit Ihrem Code kompilieren mit GCC. Sie müssen die das Datenelement initialisieren:

template<> std::map<int, int> A<char>::data = std::map<int, int>(); 

Mit dieser Änderung kompiliert und ordnungsgemäß ausgeführt wird (für mich auf GCC auf Linux).

+0

Ist dies erforderlich, weil eine separate Instanz von Daten für jede Vorlage mit einem anderen Typ initialisiert werden muss, in diesem char – Poorna

3

Es gibt keine explizite Instanziierung in Ihrem Code.

Es gibt keine Reihenfolge der Initialisierung von instanziierten statischen Datenelementen unter anderen statischen Datenelementen. Ihr Code hat also effektiv undefiniertes Verhalten: Abhängig davon, ob der Compiler zuerst die Map initialisiert oder a, ist der Verweis auf die Map gültig oder nicht.

Siehe C++ Static member initialization.

+0

Ich versuchte std :: vector anstelle von map und alles funktioniert ohne explizite Instanziierung - Sie denken, dass es nur Glück ist ? – mrs

+0

und das ist lustig, dass in diesem Beispiel die erste Zeile im Konstruktor std :: cout << data.size() << std :: endl; funktioniert wie erwartet; das Programm bricht, wenn ich versuche, etwas in die Karte einzufügen – mrs

+0

Diese "explizite Instanziierung" ist keine explizite Instanziierung. Es ist die Definition eines statischen Datenmembers namens 'data' einer expliziten Spezialisierung des Templates' A' für 'T = char'. Es gibt keine solche explizite Spezialisierung. Der Compiler muss eine Fehlermeldung für diesen Code ausgeben (wenn Sie ihn kommentieren). –

1

Es gibt mehrere Fehler in diesem Code. Zuerst ist die ursprüngliche Idee nicht gut. Sie haben zwei globale statische Objekte: a und A::data. Die Reihenfolge, in der sie initialisiert werden, ist nicht definiert. Abhängig von der Stimmung Ihres Compilers haben Sie eine Chance von 50%, dass der Konstruktor a zuerst aufgerufen wird und versucht hat, etwas in nicht initialisiertes A::data zu schreiben.

Dies wird manchmal static init order fiasco Problem genannt. Eine vorgeschlagene Lösung ist eine solche Objekte in lokale statische Objekte zu drehen, indem sie in Funktionen zu bewegen:

#include <map> 
#include <iostream> 

template <typename T> 
class A 
{ 
    std::map<int, int> &data() 
    { 
    static std::map<int, int> d; 
    return d; 
    } 
public: 
    A() 
    { 
    std::cout << data().size() << std::endl; 
    data()[3] = 4; 
    } 
}; 

int main() 
{ 
    A<char> a; 
    return 0; 
} 

Lokale statische Objekte werden initialisiert auf den ersten Aufruf der Funktion.

Über die auskommentierte "explizite Instanziierung" haben Sie template <> vergessen.

Aber nachdem Sie diese Zeile mit template <> Präfix es ist immer noch nicht Definition sondern eine Erklärung. Es erklärt, dass es A :: Datendefinition woanders gibt. Um es tatsächlich zu definieren, müssen Sie es mit etwas initialisieren, siehe zum Beispiel Jack Lloyd.