对象为模板时的静态构造

Static Construction when the object is a template

本文关键字:静态 对象      更新时间:2023-10-16

我有一个工厂类,我已经使用了很长一段时间,我试图对它做一些模板魔术,但它根本不起作用。我认为你不需要看到那个类,因为它很大,但如果你真的认为它是相关的,那么我会编辑它。无论如何,我有一个这样的设置,我已经做了很久了。

class Base :
    public Factory<Base>
{
};
class Derived :
    public Base
{
    static Factory<Base>::DerivedRegister<Derived> m_Reg;
};
Factory<Base>::DerivedRegister<Derived> Derived::m_Reg;

工厂为它提供了静态方法createInstance(conststd::string&name),它将返回已注册对象的一个实例。DerivedRegistor对象派生自工厂对象,当静态声明时,必须在静态初始化期间构造。这意味着我可以访问它的构造函数中的工厂静态内部,因此我利用它的构造函数使所有注册的对象在静态初始化期间对工厂可用。这是有效的,而且已经有一段时间了,但后来我尝试了这样的方法,这样派生类就可以避免显式生成静态deriveregister对象:

class Base :
    public Factory<Base>
{
};
template<class TDerived>
class BaseReg :
    public Base
{
    static Factory<Base>::DerivedRegister<TDerived> m_Reg;
};
template<class TDerived>
Factory<Base>::DerivedRegister<TDerived> BaseReg<TDerived>::m_Reg;
class Derived :
    public BaseReg<Derived>
{
};

当我运行该程序并尝试创建Derived类的实例时,它在工厂中并不存在,经过一点涂鸦,我发现静态DerivedRegistor从未在Derived中构造过,就像在构造函数中从未调用过一样。我觉得这很奇怪。如果派生类不是模板,怎么可能不强制它静态初始化呢?当我制作一个伪静态对象,如下所示,并在其静态构造中使用m_Reg中的方法时,DerivedRegistor突然正确构造,工厂可以实例化它,而不需要担心。

class Base :
    public Factory<Base>
{
};
template<class TDerived>
class BaseReg :
    public Base
{
protected:
    static Factory<Base>::DerivedRegister<TDerived> m_Reg;
};
template<class TDerived>
Factory<Base>::DerivedRegister<TDerived> BaseReg<TDerived>::m_Reg;
class Derived :
    public BaseReg<Derived>
{
    class RandomClass
    {
    public:
         RandomClass(std::string meh) {}
    };
private:
    static RandomClass m_Obj;
};
Derived::RandomClass Derived::m_Obj(m_Reg.func());

那么,关于这个模板类的静态成员初始化,我没有得到什么?它不需要像其他非模板对象一样静态初始化对象吗?

编辑:mkay,为了提供一点见解,我将发布Factory对象。只要小心文字墙。忽略多余的includes、多余的GetNames和func函数,它们只是voodoo代码。

#ifndef FACTORY_H
#define FACTORY_H
// library tools
#include <map>
#include <string>
#include <typeinfo>
#include <cstdlib>
#include <vector>
#include <iostream>
#include <cxxabi.h>
const std::string demangle(const char* name);
template<class base>
class Factory
{
protected:
    template<class T>
    static base * createT() { return new T;}
    typedef std::map<std::string, base*(*)()> map_type;
    static map_type& GetMap()
    {
        static map_type map;
        return map;
    }
public:
    virtual ~Factory(){}
    static base * createInstance(const std::string & s)
    {
        if(!GetMap().count(s))
            return nullptr;
        typename map_type::iterator it = GetMap().find(s);
        return it->second();
    }
    template <class TDerived>
    struct DerivedRegister :
        public Factory<base>
    {
        DerivedRegister()
        {
            std::string name = demangle(typeid(TDerived).name());
            GetMap().insert(std::pair<std::string, base*(*)()>(name, &createT<TDerived>));
        }
        DerivedRegister(const std::string& name)
        {
            GetMap().insert(std::pair<std::string, base*(*)()>(name, &createT<TDerived>));
        }
        std::string func() {return "meh";}
    };
    static void GetNames(std::vector<std::string>& names)
    {
        names.clear();
        for(auto it = GetMap().begin(); it != GetMap().end(); ++it)
            names.push_back(it->first);
    }
};

#endif

假设你是静态构建工厂本身,或者它是作为其他静态构建链的一部分构建的,那么你可能会遇到不可预测的静态初始化顺序的问题。

你可以通过包装并在它自己的函数中使用静态来绕过这个问题:

template<class TDerived>
class BaseReg :
    public Base
{
protected:
    static Factory<Base>::DerivedRegister<TDerived> & Reg()
    {
        static Factory<Base>::DerivedRegister<TDerived> m_Reg;
        return m_Reg;
    }
};

现在,访问m_Reg的唯一方法是调用Reg(),然后可以确定m_Reg已经构造完成。也就是说,它是在第一次使用的任何时间构建的。

我已经用上面的方法成功地解决了一些棘手的问题,现在我理所当然地使用它,以避免调试奇怪的崩溃。我几乎再也不需要静态成员变量了。