I'm sure I won't be bitten by this ODR violation?

I have a header file that declares a template with a static variable and also defines it:

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

This header is included by both main.cpp and the mylib shared library. In particular, mylib_baz.hpp simply includes this template and declares a function that modifies the template specialization.

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

and

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

When I do mylib.so (containing mylib_baz.o ), the symbol foo<123>::bar present and declared weak. However, the symbol foo<123>::bar declared weak also in my 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? */ } 

It looks like I'm breaking one definition rule ( foo<123>::bar , defined both in my_header.cpp and main.cpp ). However, with both g ++ and clang, characters are declared weak (or unique), so I'm not bitten by this - all calls to foo<123>::bar modify the same object.

Question 1: Is this a coincidence (maybe ODR works differently for static template members?), Or did I really guarantee this behavior by the standard?

Question 2:. How could I predict the behavior that I observe? That is, what exactly causes the compiler to declare a character weak?

+5
source share
1 answer

There are no ODR violations. You have one definition of bar . He is here:

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

Since bar is static , this means that there is one definition for all translation units. Despite the fact that bar will be displayed once in all of your object files (after all, they need a symbol for it), the linker will combine them together to be the only true definition of bar . You can see that:

 $ 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 

It produces:

 $ 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 

Where u indicates:

"U"
The symbol is a unique global symbol. This is a GNU extension for the standard ELF character binding set. For such a symbol, the dynamic linker will make sure that there is only one symbol with this name and type in use throughout the process.

+1
source

All Articles