2015-09-18 3 views
5

Ich habe eine Header-Datei, die eine Vorlage mit einer statischen Variablen deklariert und definiert auch sie:Bin ich garantiert nicht von dieser ODR-Verletzung gebissen?

/* my_header.hpp */ 
#ifndef MY_HEADER_HPP_ 
#define MY_HEADER_HPP_ 

#include <cstdio> 

template<int n> 
struct foo { 
    static int bar; 

    static void dump() { printf("%d\n", bar); } 
}; 

template<int n> 
int foo<n>::bar; 

#endif // MY_HEADER_HPP_ 

Dieser Header beide enthalten ist, durch main.cpp und eine gemeinsam genutzte Bibliothek mylib. Insbesondere enthält mylib_baz.hpp nur diese Vorlage und deklariert eine Funktion, die eine Spezialisierung der Vorlage ändert.

/* mylib_baz.hpp */ 
#ifndef MYLIB_BAZ_HPP_ 
#define MYLIB_BAZ_HPP_ 

#include "my_header.hpp" 

typedef foo<123> mylib_foo; 
void init_mylib_foo(); 

#endif // MYLIB_BAZ_HPP_ 

und

/* mylib_baz.cpp */ 
#include "mylib_baz.hpp" 
void init_mylib_foo() { 
    mylib_foo::bar = 123; 
    mylib_foo::dump(); 
}; 

Wenn ich machen mylib.so (mit mylib_baz.o), das Symbol für foo<123>::bar vorhanden ist, und erklärt schwach. Allerdings ist das Symbol für foo<123>::bar schwach erklärt auch in meinem main.o:

/* main.cpp */ 
#include "my_header.hpp" 
#include "mylib_baz.hpp" 

int main() { 
    foo<123>::bar = 456; 
    foo<123>::dump(); /* outputs 456 */ 
    init_mylib_foo(); /* outputs 123 */ 
    foo<123>::dump(); /* outputs 123 -- is this guaranteed? */ 
} 

Es scheint, dass ich eine Definition Regel bin zu verletzen (foo<123>::bar definiert sowohl in my_header.cpp und main.cpp). Sowohl mit g ++ als auch mit clang werden die Symbole jedoch als schwach (oder eindeutig) deklariert, so dass ich nicht davon gebissen werde - alle Zugriffe auf foo<123>::bar modifizieren dasselbe Objekt.

Frage 1: Ist das ein Zufall (vielleicht arbeitet ODR für statische Mitglieder von Vorlagen anders?) Oder bin ich tatsächlich garantiert dieses Verhalten durch den Standard?

Frage 2: Wie hätte ich das beobachtete Verhalten vorhersagen können? Das heißt, was genau macht der Compiler das Symbol schwach?

+1

Ich denke, der Standard sagt "Es sollte nicht funktionieren", aber Ihr Linker sagt "Es ist OK". Das ODR zu verletzen ist eine schlechte Idee - der Code wird nicht überall funktionieren. In C gibt es eine "gemeinsame Erweiterung", die die Verwendung mehrerer Definitionen ermöglicht (siehe [Wie verwende ich 'extern', um Variablen zwischen Quelldateien in C zu teilen?] (http://stackoverflow.com/questions/1433204/how-doi-i-use-extern-to-share-variables-between-source-files-in-c/) - das Feature ist wirklich eine Eigenschaft des Linker, und es gibt eine gute Chance, dass Ihre C- und C++ - Compiler die Linker-Technologie teilen.) –

+0

Einverstanden! Ich bin mir auch ziemlich sicher, dass das nicht funktionieren sollte, aber ich kann nicht wirklich sagen warum. In meinem Fall könnte (und sollte!) Ich "bar" 'extern' deklariert haben und nur in' mylib_baz.cpp' definiert haben, um dieses Problem zu lösen. Aber ich bin wirklich neugierig, tiefer zu graben und die Gründe für das beobachtete Verhalten zu sehen. – FreenodeForsakeMe

+0

"' foo <123> :: bar' definiert sowohl in my_header.cpp als auch in main.cpp "... Was? Ich sehe es an einem Ort definiert, der keiner der erwähnten ist: 'my_header.hpp'. Das ist eine Definition. – Barry

Antwort

1

Es liegt keine ODR-Verletzung vor. Sie haben eine Definition von bar. Es ist hier:

template<int n> 
int foo<n>::bar; // <== 

Wie bar ist static, dass zeigt, dass es eine Definition für alle Übersetzungseinheiten ist. Auch wenn bar einmal in allen Ihren Objektdateien angezeigt wird (sie benötigen schließlich ein Symbol), wird der Linker sie zusammenführen, um die eine wahre Definition von bar zu sein. Sie können sehen, dass:

$ g++ -std=c++11 -c mylib_baz.cpp -o mylib_baz.o 
$ g++ -std=c++11 -c main.cpp -o main.o 
$ g++ main.o mylib_baz.o -o a.out 

Produziert:

$ nm mylib_baz.o | c++filt | grep bar 
0000000000000000 u foo<123>::bar 
$ nm main.o | c++filt | grep bar 
0000000000000000 u foo<123>::bar 
$ nm a.out | c++filt | grep bar 
0000000000601038 u foo<123>::bar 

Wo u zeigt:

"u"
Das Symbol ist ein einzigartiges globales Symbol. Dies ist eine GNU-Erweiterung zum Standardsatz von ELF-Symbolbindungen. Für ein solches Symbol stellt der dynamische Linker sicher, dass im gesamten Prozess nur ein Symbol mit diesem Namen und Typ verwendet wird.

+0

'static' ist nicht genau dafür verantwortlich. Es ist, dass 'bar' ein Mitglied einer Vorlage ist und zusammen mit dieser Vorlage instanziiert (und zwischen Übersetzungseinheiten vereinheitlicht) werden kann. –

Verwandte Themen