C++ 模板类的静态成员数组的延迟初始化
我正在编写代码来执行C++ 模板类的静态成员数组的延迟初始化,c++,templates,static-members,C++,Templates,Static Members,我正在编写代码来执行n点,其中n是编译时常量 对于给定的n,我知道如何计算横坐标和权重。必须从头开始计算每个不同的n 现在,我做了以下几点: // Several structs like this one (laguerre, chebyshev, etc). template <size_t n> struct legendre { static const size_t size = n; static const double x[n]; static
n
点,其中n
是编译时常量
对于给定的n
,我知道如何计算横坐标和权重。必须从头开始计算每个不同的n
现在,我做了以下几点:
// Several structs like this one (laguerre, chebyshev, etc).
template <size_t n>
struct legendre
{
static const size_t size = n;
static const double x[n];
static const double w[n];
};
template <typename Rule, typename F>
double gauss_quadrature (F&& f)
{
double acc = 0;
for (size_t j = 0; j < Rule::size; j++)
acc += Rule::w[j] * f (Rule::x[j]);
return acc;
}
以及:
- 当我调用
时,模板gauss_quarture
会自动实例化(就是这种情况)legendre
- 当
被实例化一段编译时间legendre
时,我希望在main之前的某个点调用上面的n
,以便它填充compute\u legendre\u coefs
和x
成员数组。我如何实现这一点w
template <size_t n>
const double legendre<n>::x[n] = {};
template <size_t n>
const double legendre<n>::w[n] = {};
模板
常数双勒让德::x[n]={};
模板
常量双勒让德::w[n]={};
但是我想不出一个方法来初始化它们。有人有这样做的诀窍吗?也许您可以尝试将函数转换为初始值设定项类模板,其参数将是您要初始化的类/结构。然后更改该类模板以包含初始值设定项的常量实例。最后,让initializer类的构造函数触发执行实际初始化的代码 您可能还应该保护对initializer类的访问[1],以便初始化不会发生多次 正如您所看到的,其思想是使用以下事实:类实例调用其构造函数代码,模板实例初始化其常量数据 下面是一个可能的(简单的)实现,没有模板:
struct legendre_init {
legendre_init(){
compute_legendre_coeffs (T::n, T::w, T::x);
}
};
template <size_t n>
struct legendre
{
typedef legendre<n> self_type;
static const size_t size = n;
static const double x[n];
static const double w[n];
static const legendre_init _l;
};
struct legendre\u init{
legendre_init(){
计算勒让德系数(T::n,T::w,T::x);
}
};
模板
勒让德结构
{
typedef legendre self_类型;
静态常数size\u t size=n;
静态常数双x[n];
静态常数双w[n];
静态常数legendre_init;
};
下面是另一个例子,这次将初始化直接放在结构中:
template <class T>
class T_init {
public:
T_init(){
T::_init();
}
};
template <size_t n>
struct legendre
{
typedef legendre<n> self_type;
static const size_t size = n;
static const double x[n];
static const double w[n];
static const T_init<self_type> _f;
static void _init(){
compute_legendre_coeffs (self_type::n, self_type::w, self_type::x);
}
};
模板
类T_init{
公众:
T_init(){
T::_init();
}
};
模板
勒让德结构
{
typedef legendre self_类型;
静态常数size\u t size=n;
静态常数双x[n];
静态常数双w[n];
静态常数T_init f;
静态void _init(){
计算勒让德系数(自我类型::n,自我类型::w,自我类型::x);
}
};
此解决方案的有趣特性是T_init
常量实例不应占用T
结构中的任何空间。初始化逻辑与需要它的类捆绑在一起,而T_init
模板仅自动启用它
[1] Xeo提到了std::call_once模板,它在这里很方便。将数组转换为
std::array
:
#include <array>
template<int n> struct legendre {
static const std::array<double, n> x;
};
void compute_xs(int n, double *xs) {
...
}
template<int n> std::array<double, n> make_xs() {
std::array<double, n> xs;
compute_xs(n, xs.data());
return xs;
}
template<int n> const std::array<double, n> legendre<n>::x = make_xs<n>();
首先,在编译时,你不能用C++ 03完全完成初始化(YEP,按设计!)——唯一的方法是使用C++模板,但是你不能传递一个双模板参数。 通过C++11,情况变得更好了——您可以使用
constepr
,但前提是您的compute\u legendre\u coefs()
足够简单
或者,当需要根据类声明的事实执行某些操作时,我会使用一个技巧——例如,在某处注册smth。。。通过类似这样的库或smth提供序列化功能
您可以使用静态构造函数习惯用法初始化数组。。。出于类似的原因,我使用以下代码:
template <
typename Derived
, typename Target = Derived
>
class static_xtors
{
// This class will actually call your static init methods...
struct helper
{
helper()
{
Target::static_ctor();
}
~helper()
{
Target::static_dtor();
}
};
// ... because your derived class would inherit this member from static_xtor base
static helper s_helper;
// The rest is needed to force compiler to instantiate everything required stuff
// w/o eliminate as unused...
template <void(*)()>
struct helper2 {};
static void use_helper()
{
(void)s_helper;
}
helper2<&static_xtors::use_helper> s_helper2;
virtual void use_helper2()
{
(void)s_helper2;
}
public:
/// this is not required for your case... only if later you'll have
/// a hierarchy w/ virtuals
virtual ~static_xtors() {}
};
template <
typename Derived
, typename Target
>
typename static_xtors<Derived, Target>::helper
static_xtors<Derived, Target>::s_helper;
正如您可能注意到的,x
和w
不再是常量:(-您可以尝试使它们const
再次隐藏到private
并添加静态getter供调用方使用……此外,您的内部数组将在运行时初始化,但在main
函数之前(仅一次)
或者播放w/
constexpr
…但似乎需要重新设计初始化器函数(不知何故),因为使用初始化列表,它应该如下所示:
double i = gauss_quadrature<legendre<12>> (f);
template <size_t n>
struct legendre : public static_xtors<legendre<n>>
{
static const size_t size = n;
static double x[n];
static double w[n];
static void static_ctor()
{
compute_legendre_coeffs(n, x, w);
}
static void static_dtor()
{
// nothing to do
}
};
template <size_t n>
static double legendre<n>::x[n];
template <size_t n>
static double legendre<n>::w[n];
template <size_t n>
static double legendre<n>::x[n] = { calc_coeff_x<0>(), calc_coeff_x<1>(), calc_coeff_x<2>(), ... }
模板
静态双勒让德::x[n]={calc_coeff_x(),calc_coeff_x(),calc_coeff_x(),…}
…而且在没有专门化(以及大量使用宏)的情况下,您肯定无法做到这一点。
但可变模板可能会有所帮助……需要了解更多有关函数的详细信息,并需要时间思考:)模板
勒让德阶级
{
公众:
静态常数size\u t size=n;
静态常量双精度(&getX())[n]{
init();
返回x;
}
静态常量双精度(&getW())[n]{
init();
返回x;
}
私人:
静态双x[n];
静态双w[n];
静态void init(){
静态bool=do_init(x,y);
}
静态bool do_init(双*x,双*y){
//在这里进行计算,使用局部变量x,y
返回true;
}
};
模板
双勒让德::x[n];
模板
双勒让德::w[n];
通过提供访问器,您可以控制类的入口点。访问器分派给init
函数,该函数使用局部静态变量的初始化在程序生命周期内仅调用一次do_init
。do_init
执行成员的实际初始化
注:
根据编译器的不同,这可能不是线程安全的(也就是说,并非所有C++03编译器都提供静态变量的线程安全初始化,这反过来意味着do_init
可能会被并行调用多次,这取决于可能存在或不存在问题的算法——也就是说,如果do_init
将值计算到一边并只写入它们,则潜在的竞争条件是i一些编译器提供了保证一次性执行的机制(我相信boost已经成功了)
template <
typename Derived
, typename Target = Derived
>
class static_xtors
{
// This class will actually call your static init methods...
struct helper
{
helper()
{
Target::static_ctor();
}
~helper()
{
Target::static_dtor();
}
};
// ... because your derived class would inherit this member from static_xtor base
static helper s_helper;
// The rest is needed to force compiler to instantiate everything required stuff
// w/o eliminate as unused...
template <void(*)()>
struct helper2 {};
static void use_helper()
{
(void)s_helper;
}
helper2<&static_xtors::use_helper> s_helper2;
virtual void use_helper2()
{
(void)s_helper2;
}
public:
/// this is not required for your case... only if later you'll have
/// a hierarchy w/ virtuals
virtual ~static_xtors() {}
};
template <
typename Derived
, typename Target
>
typename static_xtors<Derived, Target>::helper
static_xtors<Derived, Target>::s_helper;
template <size_t n>
struct legendre : public static_xtors<legendre<n>>
{
static const size_t size = n;
static double x[n];
static double w[n];
static void static_ctor()
{
compute_legendre_coeffs(n, x, w);
}
static void static_dtor()
{
// nothing to do
}
};
template <size_t n>
static double legendre<n>::x[n];
template <size_t n>
static double legendre<n>::w[n];
template <size_t n>
static double legendre<n>::x[n] = { calc_coeff_x<0>(), calc_coeff_x<1>(), calc_coeff_x<2>(), ... }
template <size_t n>
class legendre
{
public:
static const size_t size = n;
static const double (&getX())[n] {
init();
return x;
}
static const double (&getW())[n] {
init();
return x;
}
private:
static double x[n];
static double w[n];
static void init() {
static bool _ = do_init(x,y);
}
static bool do_init( double *x, double *y ) {
// do the computation here, use local vars x, y
return true;
}
};
template <size_t n>
double legendre<n>::x[n];
template <size_t n>
double legendre<n>::w[n];