2015-09-18 19 views
5

Ho un file di intestazione che dichiara un modello con una variabile statica e definisce inoltre:Sono sicuro di non essere morso da questa violazione ODR?

/* 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_ 

Questa intestazione è incluso sia dal main.cpp e una biblioteca mylib condiviso. In particolare, mylib_baz.hpp include solo questo modello e dichiara una funzione che modifica una specializzazione del modello.

/* 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_ 

e

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

quando faccio mylib.so (contenente mylib_baz.o), il simbolo per foo<123>::bar è presente e ha dichiarato debole. Tuttavia, il simbolo per foo<123>::bar è dichiarato debole anche nel mio 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? */ 
} 

Sembra che sto violando una regola di definizione (foo<123>::bar definito sia in my_header.cpp e main.cpp). Tuttavia, sia con g ++ che clang i simboli sono dichiarati deboli (o unici), quindi non mi viene morso da questo: tutti gli accessi a foo<123>::bar modificano lo stesso oggetto.

Domanda 1: Si tratta di una coincidenza (forse l'ODR funziona in modo diverso per i membri statici dei modelli?) O sono effettivamente garantito questo comportamento dallo standard?

Domanda 2: Come avrei potuto prevedere il comportamento che sto osservando? Cioè, cosa rende il compilatore dichiaratamente debole?

+1

Penso che lo standard dica "Non dovrebbe funzionare" ma il tuo linker dice "Va tutto bene". Violare l'ODR è una cattiva idea - il codice non funzionerà ovunque. In C, esiste una "estensione comune" che consente il funzionamento di più definizioni (si veda [Come utilizzare 'extern' per condividere le variabili tra i file di origine in C?] (http://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files-in-c/) - la funzionalità è davvero una proprietà del linker, e c'è una buona possibilità che i compilatori C e C++ condividano la tecnologia del linker.) –

+0

Concordato! Sono anche abbastanza sicuro che questo non dovrebbe funzionare, ma non riesco davvero a capirlo perché. Nel mio caso, potrei (e dovrei!) Dichiarare 'bar' extern' e solo definirlo in' mylib_baz.cpp', risolvendo questo problema. Ma sono davvero curioso di scavare più a fondo e vedere la logica dietro il comportamento che sto osservando. – FreenodeForsakeMe

+0

"' foo <123> :: bar' definito sia in my_header.cpp che in main.cpp "... Cosa? Lo vedo definito in un posto che non è nessuno di quelli che hai citato: 'my_header.hpp'. Questa è una definizione. – Barry

risposta

1

Non vi è alcuna violazione ODR. Hai una definizione di bar. E 'qui:

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

Come bar è static, che indica che v'è una definizione in tutte le unità di traduzione. Anche se bar si visualizzerà una volta in tutti i file oggetto (dopo tutto avranno bisogno di un simbolo), il linker li unirà per essere l'unica vera definizione di bar. Si può vedere che:

$ 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 

produce:

$ 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 

Dove u indica:

"u"
Il simbolo è un simbolo globale unico. Questa è un'estensione GNU dell'insieme standard di associazioni di simboli ELF. Per tale simbolo il linker dinamico si assicurerà che nell'intero processo ci sia un solo simbolo con questo nome e tipo in uso.

+0

'static' non è ciò che è responsabile di questo, esattamente. È che 'bar' è membro di un modello e può essere istanziato (e unificato tra le unità di traduzione) insieme a quel modello. –