Please take a look at the following:
#include <string>
#include <unordered_map>
template <int N> class Object;
template <int N> class Thing;
template <int N>
class Factory {
private:
using FuncPtr = Object<N>*(*)(Thing<N>*);
static std::unordered_map<std::string, FuncPtr> map;
public:
static void insertInMap (const std::string& tag, FuncPtr funcPtr) {
map.emplace (tag, funcPtr);
}
};
template <int N>
std::unordered_map<std::string, typename Factory<N>::FuncPtr> Factory<N>::map;
// won't compile on GCC 4.8.1:
//template <> std::unordered_map<std::string, typename Factory<0>::FuncPtr> Factory<0>::map;
template <int N> struct Object {};
struct Blob : Object<0> {
static Blob prototype;
Blob() {Factory<0>::insertInMap ("Blob", Blob::create);}
Blob (Thing<0>*) {/* */}
static Object<0>* create (Thing<0>* x) {return new Blob(x);}
};
Blob Blob::prototype; // Calls up Factory<0>::insertInMap during compile time, but crashes when run.
int main()
{
}
So it appears that Blob Blob::prototype;
crashes because Factory<0>::map
has not been instantiated yet, so I try to instantiate it with the line:
template <> std::unordered_map<std::string, typename Factory<0>::FuncPtr> Factory<0>::map;
but it won't compile (with GCC 4.8.1):
C:\Users\Andy\AppData\Local\Temp\ccsGlFeV.o:Practice.cpp:(.text$_ZN7FactoryILi0E
E11insertInMapERKSsPFP6ObjectILi0EEP5ThingILi0EEE[__ZN7FactoryILi0EE11insertInMa
pERKSsPFP6ObjectILi0EEP5ThingILi0EEE]+0x14): undefined reference to `Factory<0>:
:map'
collect2.exe: error: ld returned 1 exit status
Instead of specializing
Factory<N>::map
for<0>
, just explicitly instantiate the entire class:in place of
//template <> ...
DEMO
UPDATE
For Visual Studio, which seems to still fail to initialize the static field even the template is explicitly instantiated before first usage, you can alternatively specialize the entire class:
or define the field for
Factory<0>
(though I don't know why VS accepts that and does not trigger an error, as the syntax is not valid):DEMO 2