Нечетное поведение шаблона C ++ со статическими элементами - PullRequest
2 голосов
/ 18 июня 2010

Предполагается, что этот фрагмент кода вычисляет приближение к 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?

Ответы [ 3 ]

1 голос
/ 18 июня 2010

Очевидно, вы не можете зависеть от порядка статической инициализации члена, по крайней мере, в 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

В кодовая панель , он производит:

1
1
1
1
0 голосов
/ 18 июня 2010

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;
}
0 голосов
/ 18 июня 2010

Для чего стоит, результаты с 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
...