2009-06-15 12 views
24

In C++, wenn Sie definieren diese Funktion in header.hppstatische Variable innerhalb Template-Funktion

void incAndShow() 
{ 
    static int myStaticVar = 0; 
    std::cout << ++myStaticVar << " " << std::endl; 
} 

und Sie sind header.hpp in mindestens zwei CPP-Dateien. Dann haben Sie multiple definition of incAndShow(). Was erwartet wird. Wenn Sie jedoch eine Vorlage für die Funktion

template <class T> 
void incAndShow() 
{ 
    static int myStaticVar = 0; 
    std::cout << ++myStaticVar << " " << std::endl; 
} 

fügen Sie dann werden Sie keine multiple definition of Fehler haben. Gleichermaßen teilen sich zwei verschiedene .cpp, die die Funktion mit derselben Schablone aufrufen (z. B. incAndShow<int>()), myStaticVar. Ist das normal? Ich stelle diese Frage, weil ich mich auf dieses "Feature" (das Teilen der statischen Variable) verlasse, und ich möchte sicher sein, dass nicht nur meine Implementierung dies tut.

+0

+1 Gute Frage.Im Moment implementiere ich etwas, das * auf diese Funktion angewiesen ist *. – Nawaz

Antwort

27

Darauf können Sie sich verlassen. Die ODR (One Definition Rule) sagt bei 3.2/5 im Standard, wo D steht für die nicht-statische Funktion Vorlage (Kursivguß von mir)

Wenn D eine Vorlage und ist in mehr als eine Übersetzung definiert Einheit, dann gelten die letzten vier Anforderungen aus der obigen Liste für Namen aus dem umschließenden Gültigkeitsbereich der Vorlage, der in der Vorlagendefinition (14.6.3) verwendet wird, sowie für abhängige Namen zum Zeitpunkt der Instanziierung (14.6.2).Wenn die Definitionen von D alle diese Anforderungen erfüllen, dann muss sich das Programm so verhalten, als gäbe es eine einzige Definition von D. Wenn die Definitionen von D diese Anforderungen nicht erfüllen, ist das Verhalten undefiniert.

Von den letzten vier Anforderungen, die beiden wichtigsten sind rund

  • jede Definition von D der gleichen Folge von Token
  • Namen in jeder Definition bestehen gelten die gleichen Dinge beziehen soll ("Entitäten")

bearbeiten

Ich denke, dass dies alleine nicht ausreicht, um zu garantieren, dass Ihre statischen Variablen in den verschiedenen Instanziierungen alle gleich sind. Das obige garantiert nur, dass die mehreren Definitionen der Vorlage gültig sind. Es sagt nichts über die Spezialisierungen aus, die daraus generiert werden. Diese

ist, wo Gestänge Kicks in. Wenn der Name einer Funktion Template-Spezialisierung (die eine Funktion ist) weist Außengestänge (3.5/4), dann wird ein Name, bezieht sich auf die gleiche Funktion auf eine solche Spezialisierung bezieht. Für eine Vorlage, die als statisch deklariert wurde, haben die von ihr instanziierten Funktionen interne Verknüpfungen, da

Entitäten, die aus einer Vorlage mit interner Verknüpfung generiert werden, sich von allen in anderen Übersetzungseinheiten generierten Entitäten unterscheiden. -- 14/4

einem Namen Namespacebereich (3.3.6) hat interne Bindung, wenn er den Namen [...] ein Objekt, Referenz, Funktion oder Funktionsschablone, die

explizit deklariert statische -- 3.5/3

ist, Wenn die Funktionsvorlage nicht mit statisch deklariert wurde, dann hat sie eine externe Verknüpfung (das ist übrigens auch der Grund, dass wir dem ODR überhaupt folgen müssen. Ansonsten wäre nicht mehrfach definiert!). Dies kann von 14/4 (zusammen mit 3.5/3) abgeleitet werden

ein Drittfunktionsschablone interne Bindung haben können; jeder andere Vorlagenname muss eine externe Verknüpfung haben. -- 14/4.

Schließlich kommen wir zu dem Schluss, dass eine Funktion Vorlage Spezialisierung aus einer Funktionsvorlage erzeugt mit externer Bindung hat mir externe Bindung von 3.5/4:

Ein Name Namespacebereich aufweist, hat eine externe Bindung, wenn es der Name [...] eine Funktion, es sei denn, es interne Bindung -- 3.5/4

hat Und wenn es interne Bindung von 3.5/3 für Funktionen erklärt wurde durch explizite bereitgestellt Spezialisierungen und 14/4 für generierte Spezialisierungen (Template-Instanziierungen). Da Ihr Vorlagenname eine externe Verknüpfung hat, haben alle Ihre Spezialisierungen eine externe Verknüpfung: Wenn Sie ihren Namen (incAndShow<T>) aus verschiedenen Übersetzungseinheiten verwenden, beziehen sie sich auf die gleichen Funktionen, was bedeutet, dass Ihre statischen Objekte bei jeder Gelegenheit gleich sind.

5

Nur so verstehe ich Ihre Frage. Sie fragen, ob es für jede Version der Template-Funktion normal ist, eine eigene Instanz von myStaticVar zu haben. (zum Beispiel: incAndShow<int> vs. intAndShow<float>

Ihre andere Frage ist, wenn zwei Dateien den Header enthalten, der die Template-Funktion enthält, teilen sie immer noch die statische Variable für ein gegebenes T. Ich würde ja sagen.

0

Vorlagen werden bei Bedarf instanziiert, was bedeutet, dass Compiler (Linker in diesem Fall?) Sicherstellen, dass Sie nicht mit mehreren Instanzen der gleichen Vorlage sowie nur die Instanzen der Vorlagen, die Sie sind brauche - in deinem Fall wird nur incAndShow<int>() instanziiert und sonst nichts (sonst müsste der Compiler versuchen, für jeden Typ, der keinen Sinn ergibt, instanziieren.)

Also nehme ich an, dass die gleichen Methoden, die es verwendet, um herauszufinden, für welchen Typ zu instanziieren Vorlage verhindert, dass es zweimal für den gleichen Typ instanziieren, z. Nur eine Instanz von incAndShow<int>()

Dies unterscheidet sich von Nicht-Vorlage-Code.

-3
  • Vorlagen werden nur tatsächlich in den Code eingeschaltet werden, sobald sie für die Umsetzung Code (dh verwendet)
  • Header sind nicht verwendet werden soll, instanziiert sind aber nur für Erklärungen
+0

Leute, wenn Sie mich abwerten, würde ich gerne einen Kommentar hinterlassen, was das Problem ist - offensichtlich war die ursprüngliche Frage ziemlich vage ... – none

+2

"Header werden nicht für den Implementierungscode verwendet, sondern nur für Deklarationen " Nicht zutreffend für Vorlagen. Sie müssen Templates im Header definieren ("implementieren"). Der Export war ein Weg, der sich als zu schwierig erwiesen hat und im Allgemeinen nicht umgesetzt wird. – Suma

0

Ja, es ist "normal", aber was immer Sie mit diesem "Feature" erreichen wollen, ist vielleicht fraglich. Versuchen Sie zu erklären, warum Sie eine lokale statische Variable verwenden möchten. Vielleicht können wir Ihnen einen saubereren Weg bieten, dies zu tun.

Der Grund dafür ist die Art und Weise, wie Vorlagenfunktionen kompiliert und verknüpft werden. Jede Übersetzungseinheit (die beiden .cpp in Ihrem Fall) sehen ihre eigene Kopie von incAndShow und wenn das Programm miteinander verknüpft ist, werden die beiden incAndShow zu einem zusammengefügt. Wenn Sie Ihre reguläre Funktion in der Header-Datei inline deklarieren, erhalten Sie einen ähnlichen Effekt.

1

Der Unterschied beim Erstellen der Funktionsvorlage besteht darin, dass sie extern verknüpft ist. Die gleiche incAndShow wird von allen Übersetzungseinheiten aus zugänglich sein.

Paraphrasieren von C++ Standard-Arbeitsentwurf N2798 (2008-10-04): 14 Teil 4: eine Nicht-Mitglieder-Funktionsvorlage kann interne Verknüpfung haben, andere haben immer externe Verknüpfung. 14.8 Punkt 2: Jede Spezialisierung hat ihre eigene Kopie der statischen Variable.

Ihre Funktionsvorlage sollte eine externe Verknüpfung haben, es sei denn, Sie deklarieren sie im unbenannten Namespace oder so. Also sollten Sie für jedes T, das Sie mit Ihrer Funktionsvorlage verwenden, eine statische Variable erhalten, die das Programm durchläuft. Mit anderen Worten, es ist in Ordnung, sich darauf zu verlassen, nur eine statische Variable im Programm für jede Instantiierung der Schablone zu haben (eine für T == int, eine für T == kurz, usw.).

Nebenbei kann dies zu seltsamen Situationen führen, wenn Sie incAndShow in verschiedenen Übersetzungseinheiten unterschiedlich definieren. Wenn Sie es beispielsweise so definieren, dass es in einer Datei erhöht und in einer anderen Datei dekrementiert wird (ohne die interne Verknüpfung durch das Einfügen der Funktion in den unbenannten Namespace anzugeben), teilen beide die gleiche Funktion, die zur Kompilierungszeit zufällig ausgewählt wird (Bei g ++ hängt es von der Reihenfolge ab, in der die Objektdateien in der Befehlszeile angegeben werden).

-1

Nehmen Sie dieses Beispiel, dass das Verhalten zeigt, ist absolut zu erwarten:

#include <iostream> 

template <class T> class Some 
{ 
public: 
    static int stat; 
}; 

template<class T> 
int Some<T>::stat = 10; 

void main() 
{ 
    Some<int>::stat = 5; 
    std::cout << Some<int>::stat << std::endl; 
    std::cout << Some<char>::stat << std::endl; 
    std::cout << Some<float>::stat << std::endl; 
    std::cout << Some<long>::stat << std::endl; 
} 

Sie erhalten: 5 10 10 10 10

Die oben zeigt, dass die Änderung der statischen Variablen ist nur für Typ „int“ und damit in Ihrem Falls Sie kein Problem sehen.

+3

-1: Dieses Beispiel zeigt ein statisches Klassenmitglied; Frage fragt nach statischen Variablen innerhalb einer Funktion. –

Verwandte Themen