Undefined reference to static member of class temp

2019-08-10 05:59发布

问题:

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

回答1:

Instead of specializing Factory<N>::map for <0>, just explicitly instantiate the entire class:

template class Factory<0>;

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:

template <>
class Factory<0> {
    private:
        typedef Object<0>*(*FuncPtr)(Thing<0>*);
        static std::unordered_map<std::string, FuncPtr> map;
    public:
        static void insertInMap (const std::string& tag, FuncPtr funcPtr) {
            map.emplace (tag, funcPtr);
    }
};
std::unordered_map<std::string, Factory<0>::FuncPtr> Factory<0>::map;

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):

std::unordered_map<std::string, Factory<0>::FuncPtr> Factory<0>::map;

DEMO 2