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

chr*_*tte 5 c++ inheritance templates 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();
Run Code Online (Sandbox Code Playgroud)

现在每一个需要导出类源自ExportToLua<T>T="之类要导出".例:

 class Example: public ExportToLua<Example> {
 public:
     Example();
     virtual ~Example();
     static int exportToLua();
 private:
 };
Run Code Online (Sandbox Code Playgroud)

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

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

 int Example::exportToLua() {
     std::cout << "int Example::exportToLua()" << std::endl;
     return -2;
 }
Run Code Online (Sandbox Code Playgroud)

但是,当我运行程序时,我从未看到此消息.

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

感谢您的输入,

最好的,克里斯托弗

gre*_*olf 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;
};
Run Code Online (Sandbox Code Playgroud)

它似乎在这个演示中起作用。

编辑:正如 DyP 正确指出的那样,单一定义规则在这里发挥作用,决定是否ExportToLua<T>::m_registered实例化。

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

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

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

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

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