模拟模板类的静态构造函数

Emulating static constructors for templated classes

本文关键字:静态 构造函数 模拟      更新时间:2023-10-16

我想拥有一个带有静态数据成员的模板类,并通过模拟"静态构造函数"来初始化它。对于非训练类的类别,已经回答了这一点(请参阅C 中的静态构造函数?我需要初始化私有静态对象,什么是静态构造函数?)。但是,似乎没有任何答案适用于模板类别。

以下是一个示例,它试图从先前对模板类的答案中调整"静态构造"成语。(请注意,该示例只是初始化int,并且可以在没有此类构造函数的情况下编写;但是,我需要一般的解决方案。)

#include <iostream>
struct Foo
{
    static int x;
    static struct init
    {
        init()
        {
            std::cout << "Initializing Foo..." << std::endl;
            x = 1;
        }
    } initializer;
};
int Foo::x;
Foo::init Foo::initializer;
template<int N>
struct Bar
{
    static int x;
    static struct init
    {
        init()
        {
            std::cout << "Initializing Bar..." << std::endl;
            x = N;
        }
    } initializer;
};
template<int N>
int Bar<N>::x;
template<int N>
typename Bar<N>::init Bar<N>::initializer;
int main()
{
    std::cout << Foo::x << std::endl;
    std::cout << Bar<1>::x << std::endl;
    return 0;
}

此输出:

Initializing Foo...
1
0

但我希望它能输出:

Initializing Foo...
Initializing Bar...
1
1

这是"静态初始化顺序惨败?"

的例子

no,它是不是静态初始化顺序fiasco。这仅仅是由于模板类的每个成员都是模板本身的结果,因此直到使用后才实例化。

您的代码永远不会使用init成员,因此init永远不会实例化。

但是,您的问题很容易解决:

#include <iostream>
template<int N>
struct Bar 
{
    static int x;
};
template<int N>
int Bar<N>::x= N;
int main()
{
    std::cout << Bar<1>::x << std::endl;
    return 0;
}

这为您提供了更简单的方式。

您需要解释实例化initializer

[...]
template<int N>
typename Bar<N>::init Bar<N>::initializer;
template
typename Bar<1>::init Bar<1>::initializer;
int main()
{
    std::cout << Foo::x << std::endl;
    std::cout << Bar<1>::x << std::endl;
    return 0;
}

原因是Bar<1>::x不取决于Bar<1>::initializer。因此,由于您不使用它,编译器不会实例化。实际,initializer初始化不会初始化xx是第一个零初始化,则如果initializer实例化,则x分配了一个新值。

只要initializerx实例化在同一翻译单元中实例化,就没有静态初始化惨败的风险。因此,也可以解释 x也是一个好主意。


另外,您可以将这些变量声明为静态当地人:

#include <iostream>
template<int N>
struct Bar
{
    static int x()
      {
      static int x_val;
      static struct init
        {
        init()
          {
          std::cout << "Initializing Bar..." << std::endl;
          x_val = N;
          }
        } initializer;//indeed this circumvolution is no more needed.
      return x_val;
      }
};
int main(){
    std::cout << Bar<1>::x() << std::endl;
}

但是,如果初始化不是微不足道的,则x()内的生成代码可能正在优化。


根据您的问题,您还可以将X定义为INT周围的包装器:

class int_inited{
  int val;
  public:
  int_inited(){
    std::cout << "Perfoming initialization" << std::endl;
    val=42;
    }
  operator int&(){
    return val;
    }
  operator const int &() const{
    return val;
    }
  };
template<class N>
struct Bar{
  static int_inited x;
  [...];

我找到了一个可用于任何数据类型的干净解决方案。由于当编译器遇到特定的Bar<N>::x实例化时,评估了template中的分配操作,因此我们可以写:

template<int N>
int Bar<N>::x = init<N>();

其中init()是返回intN上的函数。此外,init()仅针对编译器实例化的N的每个值调用一次。

作为一个更有用的例子,在这里我根据某些任意功能初始化静态std::array

#include <iostream>
#include <array>
template<int N>
struct Foo
{
    static std::array<double,N> x;
};
template<int N>
std::array<double,N> init()
{
    std::array<double,N> y;
    for (int i=0; i<N; ++i) {
        y[i] = (double)(i*i+i)/N;
    }
    return y;
}
template<int N>
std::array<double,N> Foo<N>::x = init<N>();
int main()
{
    const int N = 10;
    for (int i=0; i<N; ++i) {
        std::cout << Foo<N>::x[i] << std::endl;
    }
    return 0;
}