模板类静态成员变量的隐式初始化 目前我正在研究一个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();

模板类静态成员变量的隐式初始化 目前我正在研究一个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();,c++,templates,inheritance,static-members,C++,Templates,Inheritance,Static Members,其中Example的静态成员函数exportToLua()保存特定于类的注册代码。我的理解是,静态成员变量ExportToLua::m_registered的实例对于每个编译单元都存在,也就是说,对于每个T 但当我启动程序时,注册码从未被调用。例如,在example.cpp中: int Example::exportToLua() { std::cout << "int Example::exportToLua()" << std::endl; re

其中
Example
的静态成员函数
exportToLua()
保存特定于类的注册代码。我的理解是,静态成员变量
ExportToLua::m_registered
的实例对于每个编译单元都存在,也就是说,对于每个
T

但当我启动程序时,注册码从未被调用。例如,在example.cpp中:

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

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

这种行为是由C++标准支持的,这里是段落< /p>


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

以及@gx发现的另一个相关部分_

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

一个变通方法是@gx_3;提到的方法:只需添加

         ExportToLua() { (void)&m_registered; }

获取地址将强制实例化已注册的静态变量m_。

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

#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;
};
定义力实例化(x)(x) //或(避免墙壁和迂腐的警告) //模板内联void FORCE_实例化(T){ 模板 托鲁阿类 { 公众: ExportToLua(){} virtual~ExportToLua(){FORCE_实例化(m_注册);} 私人: 静态整数m_注册; };
它似乎在中工作

Edit:正如DyP正确指出的那样,无论是否实例化
ExportToLua::m_registered
,这里都会用到一个定义规则

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

  • 为要导出的类的构造函数或析构函数提供定义
  • 您创建了该类的一个实例,该实例在代码的其他部分中使用。这将强制编译器提供一个默认的ctor(如果您没有提供),从而触发必要的模板实例化
如果出于任何原因都不能满足这些条件,则需要从模板中显式实例化所需的成员。例如

class Example: public ExportToLua<Example>
{
public:
  // ...
  static int exportToLua();
  // etc.
};
template int ExportToLua<Example>::m_registered;
类示例:公共导出tolua
{
公众:
// ...
静态int exportToLua();
//等等。
};
模板int ExportToLua::m_已注册;

如果需要,您可以将其包装到宏中以使其更易于使用。

也许您必须显式实例化基,即
模板类ExportToLua;
类示例的编译单元(源文件)中。
这里回答:。Live Example:(vs)@gx_uu这个答案远远不能令人满意。它指的是一个特定的编译器,没有声明代码的合法性/正确性。14.7.1p1…类模板专门化的隐式实例化导致类成员函数的声明隐式实例化,而不是定义或默认参数的隐式实例化ns、成员类、作用域成员枚举、静态数据成员和成员模板;它会导致非作用域成员枚举和成员匿名联合定义的隐式实例化。感谢标准引用!@Walter here you are=)(我现在可以将此权威引用添加到我对另一个问题的回答中)。我想你可以发布你自己问题的答案。事实上,我刚刚发现(在N3337中)另一个相关的引用,在同一节中更进一步:14.7.1p8类模板的隐式实例化不会导致该类的任何静态数据成员被隐式实例化。我认为[temp.inst]/2是最合适的;)“除非类模板[…]的某个成员已显式实例化或显式专门化,否则当在要求成员定义存在的上下文中引用专门化时,该成员的专门化将隐式实例化;特别是初始化(以及任何相关的副作用)除非静态数据成员本身的使用方式要求静态数据成员的定义存在,否则不会出现静态数据成员的定义。”我使用GCC 4.8.1 20130725(预发布)从这个演示中没有得到任何输出在Archlinux x64上,甚至不禁用优化,除非我明确尝试读取
m_registered
,所以我认为这不是一个可靠的解决方法。-1在等待更好的解决方法时。更可靠的解决方法是强制构造一个类型为
ExportToLua
的全局对象,其默认构造函数为ould调用
T::exportToLua()
。标准要求调用构造函数。这可以通过定义宏
#define EXPORT_to_LUA(X)exportToLua##X
并在认为应该导出类时调用它来实现,如:
EXPORT_to_LUA(示例)
。注意事项:ExportToLua不能被继承,但我看到n
class Example: public ExportToLua<Example>
{
public:
  // ...
  static int exportToLua();
  // etc.
};
template int ExportToLua<Example>::m_registered;