2013-10-13 5 views
6

КодРекурсивные шаблоны не работают, как ожидается, со статическими переменными

#include <iostream> 
using namespace std; 

template<int n> struct Fibo { static int x; }; 
template<> int Fibo<0>::x = 1; 
template<> int Fibo<1>::x = 1; 
template<int n> int Fibo<n>::x = Fibo<n-1>::x + Fibo<n-2>::x; //marked line 

int main() { 
    cout << Fibo<5>::x << endl; 
    cout << Fibo<4>::x << endl; 
    cout << Fibo<3>::x << endl; 
    cout << Fibo<2>::x << endl; 
    cout << Fibo<1>::x << endl; 
    cout << Fibo<0>::x << endl; 

    return 0; 
} 

выходов

0 
0 
1 
2 
1 
1 

в VC++. (Согласно пользователю M M. он компилируется, как ожидалось, в gcc). Когда компилятор получает отмеченной линии с n=5 он не компилируется ту же самую линию снова n=4, но только лечит Fibo<4>::x, как если бы он был объявлен с

template<> int Fibo<4>::x; // x defaults to 0 

Почему это? Почему он работает должным образом при использовании

template<int n> struct Fibo { enum { x = Fibo<n-1>::x + Fibo<n-2>::x }; }; 
template<> struct Fibo<0> { enum { x = 1 }; }; 
template<> struct Fibo<1> { enum { x = 1 }; }; 

вместо этого, но не со статической переменной? И как вы исправите первый код (без enum)?

+0

Он компилируется в 'gcc-4.8.0' и вывод '' 8 5 3 2 1 1 "'. – deepmax

+2

Ничего, я никогда не знал, что вы можете специализировать инициализаторы статических элементов. –

+0

Спасибо, M M. Я добавлю, что я использовал VC++. –

ответ

3

Стандарт очень ясно по этому вопросу :

14.7.1 неявной конкретизации [temp.inst]

9 Неявное создание шаблона класса не вызывает никаких статических элементов данных этого класса, которые должны быть неявно инстанцированы.

Все звонки в main() к вашему Fibo<n>::x для n > 1, являются явными реализациями, что через рекурсию Fibonnaci неявно экземпляр Fibo<n-1> и Fibo<n-2>, но не своих членов x. Это означает, что в этих точках члены staticx будут оцениваться по умолчанию по умолчанию: 0. Для n=1 и n=0, компилятор будет настолько эффективно видеть явные значения инициализации 1. Вы получаете следующие вычисления

Fibo<5>::x --> Fibo<4>::x + Fibo<3>::x --> 0 + 0 = 0 
Fibo<4>::x --> Fibo<3>::x + Fibo<2>::x --> 0 + 0 = 0 
Fibo<3>::x --> Fibo<2>::x + Fibo<1>::x --> 0 + 1 = 1 
Fibo<2>::x --> Fibo<1>::x + Fibo<0>::x --> 1 + 1 = 2 
Fibo<1>::x --> 1 
Fibo<0>::x --> 1 

Вам нужно создать экземпляр статического члена x перед вычислением рекурсии Фибоначчей. Вы можете сделать это через static const int или enum член x или через функцию (возможно, constexpr в C++ 11), как показано @ Jarod42.

2

Я не уверен, если из template<int n> int Fibo<n>::x = Fibo<n-1>::x + Fibo<n-2>::x; указан порядок инициализации из статических переменных ...

Вы можете написать это:

template <int N> struct Fibo { int operator()() const { static int x = Fibo<N - 1>()() + Fibo<N - 2>()(); return x; } }; 

template <> struct Fibo<1> { int operator()() const { static int x = 1; return x; } }; 
template <> struct Fibo<0> { int operator()() const { static int x = 1; return x; } }; 

Зависимости уважал.

[Редактировать]

В случае, когда значение может быть изменено (в соответствии с вашим комментарием), вы можете использовать подобную технику, но возвращающуюся ссылку:

template <int N> struct Fibo { 
private: 
    int& operator()() { static int x = Fibo<N - 1>()() + Fibo<N - 2>()(); return x; } 

public: 
    int operator()() const { return const_cast<Fibo&>(*this)(); } 
    // This change Fibo<0> and Fibo<1> and then update value up to Fibo<N>. 
    int operator(int fibo0, int fibo1) { 
     int n_1 = Fibo<N - 1>()(fibo1, fibo2); 
     (*this)() = n_1 + Fibo<N - 2>()(); 
    } 
}; 

template <> struct Fibo<1> { 
private: 
    int& operator()() { static int x = 1; return x; } 
public: 
    int operator()() const { return const_cast<Fibo&>(*this)(); } 
    void operator(int fibo0, int fibo1) { Fibo<0>()(fibo0); (*this)() = fibo1; } 
}; 

template <> struct Fibo<0> { 
private: 
    int& operator()() { static int x = 1; return x; } 
public: 
    int operator()() const { return const_cast<Fibo&>(*this)(); } 
    void operator(int fibo0) { (*this)() = fibo0; } 
}; 
+0

Я использовал статические элементы вместо перечисленных типов, потому что иногда я хочу изменить их значения во время выполнения. Конечно, нет смысла менять последовательность Фибоначчи, но это был просто простой пример для гораздо более длинного кода, который у меня есть, который страдает той же проблемой. –

+1

Итак, вместо возврата 'int' вы можете вернуть' int & 'в моем примере, чтобы изменить значение позже ... – Jarod42

Смежные вопросы