Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ template recursion double linked list error gcc (but clang++ accepts)

This example code:

#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;
}

fails for 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.

but succeeds for clang:

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

Is there something wrong in the code? I wonder that gcc wants to go beyond the UPPER_LIMIT, because there is a terminating specialization for that.

like image 800
Frank Bergemann Avatar asked Jul 08 '17 16:07

Frank Bergemann


1 Answers

Wrapper<-1> is instantiated on this line

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

This causes Wrapper<0> to be instantiated, which causes Wrapper<1> to be instantiated, etc. At that point in the code, the specialization for Wrapper<UPPER_LIMIT> has not been defined, so this causes an infinite recursion.

Moving the definition of the Wrapper<UPPER_LIMIT> specialization above the Wrapper<-1>::_nxt definition solves the problem.

Apparently Clang defers instantiation, so that this problem doesn't arise.

like image 82
Vaughn Cato Avatar answered Nov 01 '22 02:11

Vaughn Cato