Consider the following piece of code (test1.cpp):
#include <string>
extern std::string test_string;
template<std::string &s>
class test{
public:
static void bar(){ }
};
std::string test_string("test string");
void foo(){test<test_string>::bar();}
Now let us switch order of two last lines of code (test2.cpp):
#include <string>
extern std::string test_string;
template<std::string &s>
class test{
public:
static void bar(){ }
};
void foo(){test<test_string>::bar();}
std::string test_string("test string");
Nothing should change. But if you look via objdump to compiled file you will see difference:
objdump -t -C test*.o | grep bar
In one case template test was instantiated as:
test<test_string[abi:cxx11]>::bar()
and in another as:
test<test_string>::bar()
both files are compiled just with
gcc -c test*.cpp
So reference to std::string as template parameter is treated as not tagged if it is just declared extern. And it is treated as tagged after definition.
Some classes in my project are instantiated twice where there should be just one class. It is rather unpleasant.
gcc version 5.2.1 20151010 (Ubuntu 5.2.1-22ubuntu2)
Is it a bug in compiler? Or is it expected behavior? What can be a workaround?
This is definitely a compiler bug; I can't find the exact bug in GCC Bugzilla but it appears similar to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66971 - though simpler, as it doesn't require the use of thread_local
keyword, it may well have the same underlying cause.
As a workaround, it appears that changing the reference template parameter to a pointer will make it work:
template<std::string *s> class test { .... };
void foo(){test<&test_string>::bar();}
Edit: I've filed this bug with gcc as https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69621