2015-09-18 10 views
5

Mam plik nagłówka, który deklaruje szablonu zmiennej statycznej, a także definiuje go:Czy mogę zagwarantować, że nie zostaniesz ugryziony przez to naruszenie 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_ 

Ten nagłówek jest włączone zarówno przez main.cpp i wspólną bibliotekę mylib. W szczególności, mylib_baz.hpp zawiera właśnie ten szablon i deklaruje funkcję, która modyfikuje specjalizację szablonu.

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

i

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

Podczas dokonywania mylib.so (zawierający mylib_baz.o) symbol foo<123>::bar jest obecny i uznane za słabe. Jednak symbolem foo<123>::bar deklaruje również słaby w moim 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? */ 
} 

Wydaje się, że jestem naruszono jedną regułę rozdzielczości (foo<123>::bar zdefiniowany zarówno w my_header.cpp i main.cpp). Jednak zarówno z g ++ jak i clang symbole są deklarowane jako słabe (lub unikalne), więc nie jestem przez to ukąszony - wszystkie dostępy do foo<123>::bar modyfikują ten sam obiekt.

Pytanie 1: Czy to przypadek (może ODR działa inaczej w przypadku statycznych członków szablonów?) Lub czy w rzeczywistości gwarantuję to zachowanie przez standard?

Pytanie 2: Jak mogłem przewidzieć zachowanie, które obserwuję? To znaczy, co dokładnie sprawia, że ​​kompilator deklaruje słaby symbol?

+1

Myślę, że standard mówi: "To nie powinno działać", ale linker mówi "Jest OK". Naruszenie ODR to zły pomysł - kod nie będzie działał wszędzie. W języku C istnieje "wspólne rozszerzenie", które umożliwia działanie wielu definicji (zobacz [Jak używać 'extern' do współdzielenia zmiennych między plikami źródłowymi w C?] (http://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files-in-c/) - funkcja jest naprawdę właściwością linker, i istnieje spora szansa, że ​​twoje kompilatory C i C++ dzielą się technologią linkera.) –

+0

Zgoda! Jestem też całkiem pewien, że to nie powinno działać, ale nie mogę tak naprawdę powiedzieć, dlaczego. W moim przypadku mogłem (i powinnam!) Zadeklarować 'bar'' extern' i zdefiniować go tylko w 'mylib_baz.cpp', rozwiązując ten problem. Ale jestem naprawdę ciekawy, jak głębiej zagłębić się i zobaczyć uzasadnienie tego zachowania, które obserwuję. – FreenodeForsakeMe

+0

"' foo <123> :: bar' zdefiniowany zarówno w my_header.cpp i main.cpp "... Co? Widzę go zdefiniowany w jednym miejscu, który nie jest jednym z wymienionych przez ciebie: 'my_header.hpp'. To jest jedna definicja. – Barry

Odpowiedz

1

Nie ma naruszenia ODR. Masz jedną definicję z . Jest tutaj:

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

Jak bar jest static, który wskazuje, że istnieje jeden definicja we wszystkich jednostek tłumaczeniowych. Chociaż bar pojawi się raz we wszystkich twoich plikach obiektowych (w końcu potrzebuje tego symbol), linker połączy je razem, aby być jedyną prawdziwą definicją bar. Widać, że:

$ 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 

Produkuje:

$ 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 

Gdzie u wskazuje:

"u"
Symbol to unikalny globalny symbol. Jest to rozszerzenie GNU do standardowego zestawu powiązań symboli ELF. Dla takiego symbolu dynamiczny linker upewni się, że w całym procesie znajduje się tylko jeden symbol o tej nazwie i typie w użyciu.

+0

'static' nie jest dokładnie tym, co jest za to odpowiedzialne. To, że 'bar' jest członkiem szablonu i może być tworzone (i ujednolicane pomiędzy jednostkami tłumaczeniowymi) wraz z tym szablonem. –

Powiązane problemy