模板类的静态成员变量的隐式初始化

时间:2013-09-02 10:09:46

标签: c++ templates inheritance static-members

目前我正在开发一个C ++项目,我计划在其中嵌入Lua脚本。因此,某些类需要导出到Lua,我想让它更方便,因此我创建了一个模板类:

template <class T>
class ExportToLua {
    public:
        ExportToLua() {}
        ~ExportToLua() {}
    private:
        static int m_registered;
};
template <class T> int ExportToLua<T>::m_registered = T::exportToLua();

现在,每个需要导出的类都派生自ExportToLua<T> T =&#34;要导出的类&#34;。例如:

 class Example: public ExportToLua<Example> {
 public:
     Example();
     virtual ~Example();
     static int exportToLua();
 private:
 };

其中Example的静态成员函数exportToLua()包含特定于类的注册码。我的理解是每个编译单元都存在一个静态成员变量ExportToLua<T>::m_registered的实例 - 即每个T

但是当我开始我的程序时,注册码永远不会被调用。例如,在example.cpp中:

 int Example::exportToLua() {
     std::cout << "int Example::exportToLua()" << std::endl;
     return -2;
 }

然而,当我运行程序时,我从未看到此消息。

知道为什么吗?编译器是否有一些&#34;优化&#34;静态变量m_registered,因为我没有在任何地方使用它?

感谢您的意见,

最佳, 克里斯托弗

2 个答案:

答案 0 :(得分:4)

如果编译器隐式实例化包含静态成员的类模板,则不会隐式实例化这些静态成员。仅当编译器需要静态成员的定义时,编译器才会实例化静态成员。

此行为由C ++标准支持,这是通道

  

14.7.1p1 ...类模板特化的隐式实例化导致声明的隐式实例化,   但不是类成员的定义或默认参数   函数,成员类,作用域成员枚举,静态   数据成员和成员模板;它导致了隐含的   实例化未作用域成员枚举的定义   成员匿名工会。

和@gx _

找到的另一个相关部分
  

14.7.1p8类模板的隐式实例化不会导致该类的任何静态数据成员被隐式实例化。

解决方法是@gx_提到的:简单地添加

         ExportToLua() { (void)&m_registered; }

到构造函数。取地址会强制静态变量m_registered的实例化。

答案 1 :(得分:3)

您已经在标准中找到了为什么行为就是这样的原因。因此,作为一种解决方法,您可以“欺骗”编译器通过从模板构造函数或析构函数引用它来实例化该静态成员。

#define FORCE_INSTANTIATE(x) (x)
// or (avoids -Wall and -pedantic warnings)
// template <typename T> inline void FORCE_INSTANTIATE(T) {}

template <class T>
class ExportToLua
{
  public:
    ExportToLua() {}
    virtual ~ExportToLua() { FORCE_INSTANTIATE(m_registered); }
  private:
      static int m_registered;
};

它似乎适用于 this demo

编辑:正如DyP正确指出的那样, One-Defintion-Rule 在这里是否ExportToLua<T>::m_registered被实例化。

为保证隐式实例化,请确保满足以下至少一个条件:

  • 为要导出的类的构造函数或析构函数提供定义
  • 您创建了该类的实例,该实例在代码的其他部分中使用。如果您没有提供默认ctor,则会强制编译器提供默认ctor,从而触发必要的模板实例化。

如果无论出于何种原因都无法满足这些条件,那么您需要从模板中显式实例化所需的成员。例如,

class Example: public ExportToLua<Example>
{
public:
  // ...
  static int exportToLua();
  // etc.
};
template int ExportToLua<Example>::m_registered;

您可以将其包装到宏中,以便在需要时更好地使用它。