【问题标题】:Undefined reference to static member of class template referenced from static instance对从静态实例引用的类模板的静态成员的未定义引用
【发布时间】:2014-09-13 16:19:50
【问题描述】:

请看以下内容:

#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()
{
}

所以看起来Blob Blob::prototype; 崩溃是因为Factory&lt;0&gt;::map 还没有被实例化,所以我尝试用以下行实例化它:

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

但它不会编译(使用 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

【问题讨论】:

  • 编译器崩溃 ???或者你只是得到一堆编译器错误消息?
  • Compiles fine here too。可执行文件崩溃了——也许这就是 OP 的意思?
  • 当您在未注释的行中发表评论时,您会收到一个 link 错误:coliru.stacked-crooked.com/a/25824e6fba8f074d
  • 当我取消注释我提到的那一行时,我发布了编译错误消息。没有那行,它会编译,但是当我运行它时会崩溃。
  • 静态初始化顺序的“乐趣”...

标签: c++ templates linker-errors undefined-reference static-order-fiasco


【解决方案1】:

不是专门化 Factory&lt;N&gt;::map&lt;0&gt;,而是显式实例化整个类:

template class Factory<0>;

代替//template &lt;&gt; ...

DEMO


更新

对于 Visual Studio,即使在第一次使用之前显式实例化模板,它似乎仍然无法初始化静态字段,您也可以专门化整个类:

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;

或者为Factory&lt;0&gt;定义字段(虽然我不知道为什么VS接受它并且不会触发错误,因为语法无效):

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

DEMO 2

【讨论】:

  • 是的,此解决方案适用于 GCC。但是当我在 Visual Studio 2013 上运行它时,它仍然因为读取 nullptr 而崩溃。哪个编译器在这里被窃听?是否有适用于两者的解决方案?因为我用两个编译器检查了我的编译。
  • 静态顺序初始化惨败只发生在不同编译单元的静态变量上
  • 好的,我将像我最初所做的那样单独专门化所有不同的工厂类,并尝试使用私有继承来捕获常见的东西。 VS2013 实际上接受了我上面的原始代码(使用了注释掉的行),但我知道代码是错误的。谢谢。
猜你喜欢
  • 2014-02-17
  • 2014-01-05
  • 2021-07-10
相关资源
最近更新 更多