模拟模板化的静态构造函数 类
Emulating static constructors for templated classes
我想要一个带有静态数据成员的模板化 class,并通过模拟一个 "static constructor." 来初始化它 对于非模板化 class,这已经得到回答(参见 static constructors in C++? I need to initialize private static objects and What is a static constructor?).但是,none 的答案似乎适用于模板化的 class。
以下是一个示例,它试图将先前答案中的 "static constructor" 习语改编为模板化的 class。 (请注意,该示例只是初始化一个 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
这是"static initialization order fiasco?"
的例子吗
不,不是静态初始化顺序失败。这只是模板 class 的每个成员本身就是一个模板这一事实的结果,因此在使用之前不会实例化。
您的代码从不使用 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
初始化并没有初始化 x
。 x
首先零初始化,然后如果initializer
被实例化,x
被赋值一个新值.
只要 initializer
在与 x
被 实例化 相同的翻译单元中实例化,就没有静态初始化失败的风险。所以显式实例化 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;
[...];
我找到了适用于任何数据类型的干净解决方案。由于 template
中的赋值操作是在编译器遇到要实例化的特定 Bar<N>::x
时计算的,我们可以这样写:
template<int N>
int Bar<N>::x = init<N>();
其中 init()
是在 N
上模板化的函数,即 returns 和 int
。此外,对于编译器实例化的 N
的每个值,init()
只会被调用一次。
作为一个更有用的例子,这里我根据一些任意函数初始化一个静态的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;
}
我想要一个带有静态数据成员的模板化 class,并通过模拟一个 "static constructor." 来初始化它 对于非模板化 class,这已经得到回答(参见 static constructors in C++? I need to initialize private static objects and What is a static constructor?).但是,none 的答案似乎适用于模板化的 class。
以下是一个示例,它试图将先前答案中的 "static constructor" 习语改编为模板化的 class。 (请注意,该示例只是初始化一个 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
这是"static initialization order fiasco?"
的例子吗不,不是静态初始化顺序失败。这只是模板 class 的每个成员本身就是一个模板这一事实的结果,因此在使用之前不会实例化。
您的代码从不使用 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
初始化并没有初始化 x
。 x
首先零初始化,然后如果initializer
被实例化,x
被赋值一个新值.
只要 initializer
在与 x
被 实例化 相同的翻译单元中实例化,就没有静态初始化失败的风险。所以显式实例化 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;
[...];
我找到了适用于任何数据类型的干净解决方案。由于 template
中的赋值操作是在编译器遇到要实例化的特定 Bar<N>::x
时计算的,我们可以这样写:
template<int N>
int Bar<N>::x = init<N>();
其中 init()
是在 N
上模板化的函数,即 returns 和 int
。此外,对于编译器实例化的 N
的每个值,init()
只会被调用一次。
作为一个更有用的例子,这里我根据一些任意函数初始化一个静态的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;
}