这段代码应该在编译时使用以下方法计算 e 的近似值(即数学常数~2.71828183);
e1 = 2 / 1
e2 = (2 * 2 + 1) / (2 * 1) = 5 / 2 = 2.5
e3 = (3 * 5 + 1) / (3 * 2) = 16 / 6 ~ 2.67
e4 = (4 * 16 + 1) / (4 * 6) = 65 / 24 ~ 2.708
...
e(i) = (e(i-1).numer * i + 1) / (e(i-1).denom * i)
计算是通过result
静态成员返回的,然而,在2次迭代后,它产生零而不是预期值。我添加了一个静态成员函数f()来计算相同的值,并且没有出现同样的问题。
#include <iostream>
#include <iomanip>
// Recursive case.
template<int Iters, int Num = 2, int Den = 1, int I = 2>
struct CalcE
{
static const double result;
static double f () {return CalcE<Iters, Num * I + 1, Den * I, I + 1>::f ();}
};
template<int Iters, int Num, int Den, int I>
const double CalcE<Iters, Num, Den, I>::result = CalcE<Iters, Num * I + 1, Den * I, I + 1>::result;
// Base case.
template<int Iters, int Num, int Den>
struct CalcE<Iters, Num, Den, Iters>
{
static const double result;
static double f () {return result;}
};
template<int Iters, int Num, int Den>
const double CalcE<Iters, Num, Den, Iters>::result = static_cast<double>(Num) / Den;
// Test it.
int main (int argc, char* argv[])
{
std::cout << std::setprecision (8);
std::cout << "e2 ~ " << CalcE<2>::result << std::endl;
std::cout << "e3 ~ " << CalcE<3>::result << std::endl;
std::cout << "e4 ~ " << CalcE<4>::result << std::endl;
std::cout << "e5 ~ " << CalcE<5>::result << std::endl;
std::cout << std::endl;
std::cout << "e2 ~ " << CalcE<2>::f () << std::endl;
std::cout << "e3 ~ " << CalcE<3>::f () << std::endl;
std::cout << "e4 ~ " << CalcE<4>::f () << std::endl;
std::cout << "e5 ~ " << CalcE<5>::f () << std::endl;
return 0;
}
我已经使用VS 2008和VS 2010对此进行了测试,并在每种情况下获得相同的结果:
e2 ~ 2
e3 ~ 2.5
e4 ~ 0
e5 ~ 0
e2 ~ 2
e3 ~ 2.5
e4 ~ 2.6666667
e5 ~ 2.7083333
为什么result
没有产生预期值,而f()
呢?
根据Rotsor下面的评论,这确实适用于GCC,所以我想问题是,我是否依赖于某些类型的静态初始化顺序的未定义行为,或者这是一个错误Visual Studio?
答案 0 :(得分:1)
显然你不能依赖于静态成员初始化的顺序,至少在VC ++中是这样。
这是一个简化的例子:
#include <stdio.h>
template<int N>
struct one
{
static const int res;
};
template<>
struct one<0>
{
static const int res;
};
template<int N>
const int one<N>::res = one<N-1>::res;
const int one<0>::res = 1;
int main()
{
printf("%d\n", one<3>::res);
printf("%d\n", one<2>::res);
printf("%d\n", one<1>::res);
printf("%d\n", one<0>::res);
}
在VC ++ 2008中,它产生:
0
1
1
1
在codepad中,它会产生:
1
1
1
1
答案 1 :(得分:0)
对于它的价值,g ++ 4.4.1的结果:
e2 ~ 2
e3 ~ 2.5
e4 ~ 2.6666667
e5 ~ 2.7083333
e2 ~ 2
e3 ~ 2.5
e4 ~ 2.6666667
e5 ~ 2.7083333
答案 2 :(得分:0)
C ++不喜欢非整数编译时常量。一种可能的解决方案是使用有理算术:
#include <iostream>
#include <iomanip>
template<int Iters, int Num = 2, int Den = 1, int I = 2>
struct CalcE
{
typedef CalcE<Iters, Num * I + 1, Den * I, I + 1> res;
enum { num = res::num, den = res::den };
static double g() { return static_cast<double>(num) / den; }
};
template<int Iters, int Num, int Den>
struct CalcE<Iters, Num, Den, Iters>
{
enum { num = Num, den = Den };
static double g() { return static_cast<double>(num) / den; }
};
int main (int argc, char* argv[])
{
std::cout << std::setprecision (8);
std::cout << "e2 ~ " << CalcE<2>::g() << std::endl;
std::cout << "e3 ~ " << CalcE<3>::g() << std::endl;
std::cout << "e4 ~ " << CalcE<4>::g() << std::endl;
std::cout << "e5 ~ " << CalcE<5>::g() << std::endl;
std::cout << "e5 ~ " << CalcE<6>::g() << std::endl;
return 0;
}