sme*_*lin 4 c++ static factory
为什么以下代码会异常引发异常(在createObjects调用中map::at
),代码(及其输出)可以在这里查看
有趣的是,如果注释行与microsoft和gcc编译器一起取消注释(参见此处),代码将按预期工作,这甚至可以将initMap用作普通静态变量而不是静态getter.
我可以想到的唯一原因是静态registerHelper_ object(factory_helper_
)和std::map
object(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;
}
Run Code Online (Sandbox Code Playgroud)
该问题与初始化顺序无关,而是与模板实例化有关.
模板化代码是按需实例化的,也就是说,编译器不会实例化程序中未使用的任何模板化代码.特别是,在你的情况下,静态类成员FactoryBase<>::factory_helper_
没有被实例化,因此它不存在于最终的二进制文件中,它不会自己注册...(你可以用gnu工具链中的'nm'来检查它,这将显示您的可执行文件中存在的符号列表)
尝试将FactoryBase
构造函数更改为:
template <class TClass, const char* ClassName>
class FactoryBase {
//...
FactoryBase(){
factory_helper_;
}
//...
};
Run Code Online (Sandbox Code Playgroud)
这将强制编译器实际实例化二进制文件中的静态成员,您应该设置.无需创建空方法并调用它.
编辑:作为评论的答案,在当前标准的段落§14.7.1[temp.inst]/1的末尾:
除非已显式实例化或明确专门化类模板或成员模板的成员,否则在需要成员定义存在的上下文中引用特化时,将隐式实例化成员的特化; 特别是,除非静态数据成员本身以需要静态数据成员的定义存在的方式使用,否则不会发生静态数据成员的初始化(以及任何相关的副作用).
归档时间: |
|
查看次数: |
863 次 |
最近记录: |