【问题标题】:trying to force static object initialization试图强制静态对象初始化
【发布时间】:2012-03-03 20:43:08
【问题描述】:

我试图初始化一个静态对象但没有成功。目的是在存储库中自动注册一个工厂类(这是一个单例)。

我已经看过了:How to force a static member to be initialized?

其中一个 cmets 说(还有一个我遵循的示例):

我在 C++ 标准 (14.7.1) 中读到它:除非类模板或成员模板的成员已显式实例化或显式特化,否则在引用特化时会隐式实例化成员的特化需要成员定义存在的上下文;特别是,静态数据成员的初始化(以及任何相关的副作用)不会发生,除非该静态数据成员本身的使用方式要求该静态数据成员的定义存在。

所以我正在尝试做类似的事情,但我没有设法强制对象初始化。这是代码。我不知道我错过了什么。这是我正在使用的模板。

namespace my_lib
{
    template <typename T>
    struct FactoryHelper
    {
        FactoryHelper ();
        static FactoryHelper<T> _helper;
    };
}

这是库用户用来定义工厂类并同时在存储库中注册对象的宏:

#define CREATE_FACTORY(ClassName)\
namespace my_lib\
{\
    class ClassName##Factory;\
    template<> FactoryHelper<ClassName##Factory>::FactoryHelper () { std::cout << "object initialized!" << std::endl; }\
    template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper;\
    struct ClassName##Factory : public FactoryBase<ClassName> {\
      ...\
    };\
} 

前面的代码是在一个头文件(Factory.h)中定义的。

在 .cpp 文件(Example.cpp)中,我有:

CREATE_FACTORY(UnitTestExample)
...

当我执行程序时,我看不到构造函数在调用时打印的消息。任何帮助都非常受欢迎。

提前致谢。

【问题讨论】:

    标签: c++ templates initialization static-members


    【解决方案1】:

    这是 C++ 的一个棘手领域。您所做的是尝试在此处定义静态成员:

    template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper;\
    

    但这实际上是一个声明,而不是一个定义。为了让 C++ 将其视为定义,您必须将某些内容传递给构造函数。通常,这是您要将其初始化为的值:

    template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper = FactoryHelper<ClassName##Factory>();\
    

    但在你的情况下,你希望这是一个单例,所以你可能不希望它是可复制的。在这种情况下,您需要一些虚拟参数:

    template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\
    

    你必须适当地修改你的构造函数:

    template<> FactoryHelper<ClassName##Factory>::FactoryHelper (int) { std::cout << "object initialized!" << std::endl; }\
    

    这是完整的工作示例:

    #include <iostream>
    
    namespace my_lib
    {
        template<typename> struct FactoryBase { };
        template <typename T>
        struct FactoryHelper
        {
            FactoryHelper (int);
            static FactoryHelper<T> _helper;
        };
    }
    
    #define CREATE_FACTORY(ClassName)\
    namespace my_lib\
    {\
        class ClassName##Factory;\
        template<> FactoryHelper<ClassName##Factory>::FactoryHelper (int) { std::cout << "object initialized!" << std::endl; }\
        template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\
        struct ClassName##Factory : public FactoryBase<ClassName> {\
        };\
    } 
    
    struct UnitTestExample {
    };
    
    CREATE_FACTORY(UnitTestExample);
    
    int main(int argc,char **argv)
    {
      return 0;
    }
    

    也就是说,使用其他答案中的一些建议可能是更好的设计决策。

    关于显式特化声明与定义的更多信息可以在这里找到:static member initialization for specialized template class

    【讨论】:

    • 嗯,首先非常感谢您的建议和解释。我将您给我的解决方案添加到代码中但没有用。然后我尝试做独立程序并工作。不同之处在于我正在实现的类被编译,然后作为静态库链接到可执行文件。如果我一起编译代码(不使用静态库),那么它可以工作。为什么这会影响静态对象初始化?谢谢!
    • 我想我在这里找到了回复:stackoverflow.com/questions/1804606/…
    【解决方案2】:

    您的宏所做的是声明一个类的某些成员的特化。这不会创建任何对象,也可能不是您真正想要的。您需要在某处定义FactoryHelper&lt;SomeClass&gt;::_helper。静态成员的定义如下所示:

    FactoryHelper<foo> FactoryHelper<foo>::_helper;
    

    也就是说,我认为这根本不是要走的路:您真正需要的是实例化一些注册工厂函数的东西,这可以更简单地完成,尤其是在没有宏的情况下。

    我会这样做:

    template <typename T>
    struct factory_helper
    {
        std::auto_ptr<base> create_fuction() { return std::auto_ptr<base>(new T()); }
        factory_helper(std::string const& name) {
            factory.register_class(name, create_function);
        }
    };
    

    这假定您要创建从类型 base 派生的对象,并且您的工厂使用映射到返回 std::auto_ptr&lt;base&gt; 作为构造函数的函数对象,并且它有一个带有名称和构造函数的 register_class() 函数函数作为参数。但是,这些假设都不是该方法所固有的:这只是为了填补您未提及的一些空白。您可以为 foo 类注册一个工厂函数,如下所示:

    static factor_helper<foo> foo_helper("foo");
    

    【讨论】:

      【解决方案3】:

      而不是静态成员(您必须在某处创建),考虑将静态变量转换为静态函数的可能性,例如

      namespace my_lib
      {
          template <typename T>
          struct FactoryHelper
          {
              FactoryHelper () { ... };
              static FactoryHelper<T>& helper()
              { static FactoryHelper<T> h; return h; }
          };
      }
      

      与您要求的不同,但不需要带外初始化。

      【讨论】:

        【解决方案4】:

        嗯,首先非常感谢您的建议和解释。我将您给我的解决方案添加到代码中但没有用。然后,我将您的解决方案作为独立程序进行了尝试并开始工作。

        不同之处在于我正在实现的类是经过编译的,然后作为静态库链接到可执行文件。如果我一起编译代码(不使用静态库),那么它可以工作。

        我在这里找到了回复:Static initialization and destruction of a static library's globals not happening with g++

        除非从主应用程序中引用它们,否则不会链接 .o 文件。我使用了ld 选项-Wl,--whole-archive,现在它可以工作了。

        -Wl,--whole-archive -lmy_static_library ... -Wl,--no-whole-archive
        

        关于第二个问题,我还是不明白为什么要在构造函数中指定一个dummy参数。

        template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\
        

        而不是这样做:

        emplate<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper = FactoryHelper<ClassName##Factory>();\
        

        谢谢!

        【讨论】:

        • 声明与定义的问题相当棘手。我在任何地方都没有找到好的解释。我想更详细地解释一下,但这似乎超出了我最初答案的范围。
        • 其实这个解释还不错:stackoverflow.com/questions/2342550/…
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-19
        • 1970-01-01
        • 2011-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多