2017-07-08 2 views
6

Cet exemple de code:C++ récursion gcc à double liste chaînée d'erreur de modèle (mais clang ++ accepte)

#include <string> 
#include <iostream> 

template <int i> 
struct Wrapper 
{ 
    static const std::string _str; 
    typedef const Wrapper<i+1> NextType_t; // template recursion 
    static NextType_t _nxt; 
    typedef const Wrapper<i-1> PrevType_t; // template recursion 
    static PrevType_t _prev; 
}; 

template<int i> 
const std::string Wrapper<i>::_str = std::to_string(i); 

template<int i> 
typename Wrapper<i>::NextType_t Wrapper<i>::_nxt; 

template<int i> 
typename Wrapper<i>::PrevType_t Wrapper<i>::_prev; 


// recursion termination - lower bound 
template <> 
struct Wrapper<-1> 
{ 
     static const std::string _str; 
     typedef const Wrapper<0> NextType_t; 
     static NextType_t _nxt; 
     typedef const Wrapper<-1> PrevType_t; 
     static PrevType_t _prev; 
}; 

const std::string Wrapper<-1>::_str = std::to_string(-1); 

typename Wrapper<-1>::NextType_t Wrapper<-1>::_nxt; 
typename Wrapper<-1>::PrevType_t Wrapper<-1>::_prev; 

// recursion termination - upper bound 
template <> 
struct Wrapper<UPPER_LIMIT> 
{ 
    static const std::string _str; 
    typedef const Wrapper<-1> NextType_t; 
    static NextType_t _nxt; 
    typedef const Wrapper<UPPER_LIMIT-1> PrevType_t; 
    static PrevType_t _prev; 
}; 

const std::string Wrapper<UPPER_LIMIT>::_str = std::to_string(UPPER_LIMIT); 

typename Wrapper<UPPER_LIMIT>::NextType_t Wrapper<UPPER_LIMIT>::_nxt; 
typename Wrapper<UPPER_LIMIT>::PrevType_t Wrapper<UPPER_LIMIT>::_prev; 


int 
main(
    int argc, 
    char **) 
{ 
    Wrapper<0> wrapperFirst; 
    Wrapper<UPPER_LIMIT> wrapperLast; 

    // here's the list 
    std::cout << wrapperFirst._str << std::endl; 
    std::cout << wrapperFirst._nxt._str << std::endl; 
    std::cout << wrapperFirst._nxt._nxt._str << std::endl; 
    // [...] 

    // and the final element 
    std::cout << wrapperLast._str << std::endl; 
    std::cout << wrapperLast._prev._str << std::endl; 
    std::cout << wrapperLast._prev._prev._str << std::endl; 
    // [...] 

    // and the tailing NIL 
    std::cout << Wrapper<UPPER_LIMIT>::NextType_t::_str << std::endl; 

    return 0; 
} 

échoue pour gcc:

> g++ -std=c++11 -DUPPER_LIMIT=100 -ftemplate-depth=500 -o test main.cpp 
main.cpp: In instantiation of ‘struct Wrapper<499>’: 
main.cpp:24:33: recursively required from ‘struct Wrapper<1>’ 
main.cpp:24:33: required from ‘struct Wrapper<0>’ 
main.cpp:43:47: required from here 
main.cpp:24:33: fatal error: template instantiation depth exceeds maximum of 500 (use -ftemplate-depth= to increase the maximum) 
typename Wrapper<i>::NextType_t Wrapper<i>::_nxt; 
          ^~~~~~~~~~ 
compilation terminated. 

mais réussit pour clang:

> clang++ -std=c++11 -DUPPER_LIMIT=100 -ftemplate-depth=500 -o test main.cpp 
> ./test 
0 
1 
2 
100 
99 
98 
-1 

Y a-t-il quelque chose qui ne va pas dans le code? Je me demande si gcc veut aller au-delà de UPPER_LIMIT, car il y a une spécialisation de terminaison pour cela.

+2

Probablement parce que vous définissez les variables de membre statiques pour 'Wrapper <-1>' avant de définir la spécialisation 'Wrapper '. Que se passe-t-il si vous définissez toutes les structures Wrapper, puis les statistiques pour les classes? – 1201ProgramAlarm

+0

@ 1201ProgramAlarm Vous êtes sur la bonne voie, mais c'est la * définition * des membres statiques, pas leur déclaration qui cause le problème (probablement) – Frank

+0

merci! - à vous deux, en répondant au même point que la réponse ci-dessous –

Répondre

4

Wrapper<-1> est instancié sur cette ligne

typename Wrapper<-1>::NextType_t Wrapper<-1>::_nxt; 

Cela provoque Wrapper<0> être instancié, ce qui provoque Wrapper<1> être instancié, etc. A ce moment-là dans le code, la spécialisation pour Wrapper<UPPER_LIMIT> n'a pas été défini, donc cela provoque une récursion infinie. Déplacer la définition de la spécialisation Wrapper<UPPER_LIMIT> au-dessus de la définition Wrapper<-1>::_nxt permet de résoudre le problème.

Apparemment, Clang diffère instanciation, de sorte que ce problème ne se pose pas.

+0

merci beaucoup! J'ai également séparé la déclaration de classe en * .h –