为什么以下代码引发异常(在createObjects调用map::at
中)
可选地,可以查看代码(及其输出)here
如果注释行使用microsoft和gcc编译器取消注释(参见here),代码可以按预期工作,这甚至可以将initMap用作普通的静态变量而不是静态getter。
我能想到的唯一原因是静态registerHelper_对象(factory_helper_
)和std::map
对象(initMap
)的初始化顺序是错误的,但是我无法看到这可能发生的原因,因为map对象是在第一次使用时构建的,那就是在factory_helper_构造函数中构建的,所以一切都应该没问题呢?
我甚至更惊讶那些doNothing()行解决了这个问题,因为在关键部分(当前失败)之后无论如何都会调用doNothing()。
编辑:调试显示,没有调用factory_helper_.doNothing(),就不会调用factory_helper_的构造函数。
#include <iostream>
#include <string>
#include <map>
#define FACTORY_CLASS(classtype) \
extern const char classtype##_name_[] = #classtype; \
class classtype : FactoryBase<classtype,classtype##_name_>
namespace detail_
{
class registerHelperBase
{
public:
registerHelperBase(){}
protected:
static std::map<std::string, void * (*)(void)>& getInitMap() {
static std::map<std::string, void * (*)(void)>* initMap = 0;
if(!initMap)
initMap= new std::map<std::string, void * (*)(void)>();
return *initMap;
}
};
template<class TParent, const char* ClassName>
class registerHelper_ : registerHelperBase {
static registerHelper_ help_;
public:
//void doNothing(){}
registerHelper_(){
getInitMap()[std::string(ClassName)]=&TParent::factory_init_;
}
};
template<class TParent, const char* ClassName>
registerHelper_<TParent,ClassName> registerHelper_<TParent,ClassName>::help_;
}
class Factory : detail_::registerHelperBase
{
private:
Factory();
public:
static void* createObject(const std::string& objclassname) {
return getInitMap().at(objclassname)();
}
};
template <class TClass, const char* ClassName>
class FactoryBase {
private:
static detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> factory_helper_;
static void* factory_init_(){ return new TClass();}
public:
friend class detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName>;
FactoryBase(){
//factory_helper_.doNothing();
}
virtual ~FactoryBase(){};
};
template <class TClass, const char* ClassName>
detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> FactoryBase<TClass,ClassName>::factory_helper_;
FACTORY_CLASS(Test) {
public:
Test(){}
};
int main(int argc, char** argv) {
try {
Test* test = (Test*) Factory::createObject("Test");
}
catch(const std::exception& ex) {
std::cerr << "caught std::exception: "<< ex.what() << std::endl;
}
#ifdef _MSC_VER
system("pause");
#endif
return 0;
}
答案 0 :(得分:7)
问题与初始化顺序无关,而是与模板实例化有关。
模板化代码是按需实例化的,也就是说,编译器不会实例化程序中未使用的任何模板化代码。特别是,在你的情况下,静态类成员FactoryBase<>::factory_helper_
没有被实例化,因此它不存在于最终二进制文件中,它不会自己注册...(你可以用gnu中的'nm'来检查它工具链,将显示可执行文件中存在的符号列表
尝试将FactoryBase
构造函数更改为:
template <class TClass, const char* ClassName>
class FactoryBase {
//...
FactoryBase(){
factory_helper_;
}
//...
};
这会强制编译器实际实例化二进制文件中的静态成员,你应该设置它。无需创建空方法并调用它。
编辑:作为评论的答案,在当前标准的段落§14.7.1[temp.inst] / 1 的末尾:
除非是类模板的成员或 成员模板已明确 实例化或明确地 专业化,专业化 当成员被隐式实例化时 专业化在a中引用 需要会员的上下文 定义存在;特别是, 初始化(和任何相关的 副作用)静态数据成员 除非静态数据不会发生 成员本身就是以某种方式使用的 需要静态的定义 数据成员存在。
答案 1 :(得分:0)
#define FACTORY_CLASS(classtype) \
class classtype; \
extern const char classtype##_name_[] = #classtype; \
template detail_::registerHelper_<FactoryBase<classtype,classtype##_name_>,classtype##_name_> FactoryBase<classtype,classtype##_name_>::factory_helper_; \
class classtype : FactoryBase<classtype,classtype##_name_>
显式实例化factory_helper_修复了问题。