dimanche 2 octobre 2016

GCC 6.1 explicit template instantiation gives undefined symbol for default constructor

Put this in a file called t.hpp:

#include <unordered_map>

extern template class std::unordered_map<int, int>;

std::unordered_map<int, int> getMap();

And this in t.cpp:

#include "t.hpp"

std::unordered_map<int, int> getMap()
{
    return std::unordered_map<std::string, const Foo*>();
}

template class std::unordered_map<std::string, const Foo*>;

Then build it (on Linux):

g++ -std=c++11 -shared -fPIC -o t.so t.cpp

Then look at the constructors in the shared object:

objdump --syms --demangle t.so | grep '::unordered_map()'

If you compiled with GCC 4.9 it should show nothing at all. With GCC 5.1 or 5.3, it should show something like this:

8a w F .text 1b std::unordered_map<int, int>::unordered_map()

But with GCC 6.1 it shows something like this:

0 *UND* 0 std::unordered_map<int, int>::unordered_map()

That means the default constructor for the map is not emitted in the shared object, though it needs to be. This causes linking failures later, when using the shared object in an executable.

If you compile with -O1 or higher, the problem goes away (the constructor is inlined).

It seems like the explicit template instantiation in t.cpp is inhibited by the no-implicit declaration in t.hpp. A workaround I came up with is to #define SOMETHING in t.cpp before #include "t.hpp" then use #ifndef SOMETHING to guard the no-implicit declaration in t.hpp. But this wasn't necessary with GCC before version 6, and I'm not sure it should be.

Is this a bug in GCC 6.1? Or is the code wrong?

Aucun commentaire:

Enregistrer un commentaire