我正在编写代码以使用n
点执行Gaussian integration,其中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;
}
像这样使用:
double i = gauss_quadrature<legendre<12>> (f);
现在,我可以通过翻译来专门研究
legendre<12>
的系数template <>
const legendre<12>::x[12] = { ... };
template <>
const legendre<12>::w[12] = { ... };
只要我只使用12点高斯-勒格德勒(Gauss-Legendre),一切都很好。
现在,我正在尝试使用不同数量的点,并且知道如何生成权重和节点。例如,我可以提供一个例程
void compute_legendre_coeffs (size_t n, double* w, double* x);
和:
gauss_quadrature<legendre<n>>
时,模板legendre<n>
会自动实例化(这种情况)。 legendre<n>
实例化n
时,我希望在main之前的某个时刻调用上述compute_legendre_coeffs
,以便它填充x
和w
成员数组。 我该如何实现? 我知道必须先定义数组:
template <size_t n>
const double legendre<n>::x[n] = {};
template <size_t n>
const double legendre<n>::w[n] = {};
但我无法提出一种初始化它们的方法。有人有技巧吗?
最佳答案
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];
通过提供访问器,您可以控制类的入口点。访问器分派(dispatch)给
init
函数,该函数使用局部静态变量的初始化在程序生命周期中仅调用一次do_init
。 do_init
进行成员的实际初始化。笔记:
取决于编译器,这可能不是线程安全的(即,并非所有的C++ 03编译器都提供静态变量的线程安全的初始化,这反过来意味着
do_init
可能被并行调用多次,具体取决于所使用的算法还是不成问题-例如,如果do_init
将这些值放在一边,只写它们,则潜在的竞争条件就无关紧要了,因为最终结果将是相同的)。一些编译器提供了保证一次性执行的机制(我相信boost具有这种机制)。或者,根据您的域,您也许可以在启动线程之前准备系数。在这种情况下,实际数组不能为
const
,因为在创建它们之后需要进行初始化。除了可能的微优化以外,这应该不是问题(即编译器不知道系数的值,因此它不能在编译时执行子表达式评估)。