Search code examples
c++templatesg++static-compilation

g++ goes too far when compiling


I'm trying to implement very simple single inheritance stack tracing in C++ using a recursive template:

#include <iostream>
using namespace std;
template <class C> struct MakeAlias : C{ typedef C Base; };
class StackTrace{
      public:
      static int var;
      virtual ~StackTrace() {}
      template <class T> void printStackTrace(T* c){
          if(typeid(T)==typeid(StackTrace))return; 
          cout << typeid(T).name() << "." << endl;
          class T::Base *V;
          printStackTrace(V);
     }
};
class A : public MakeAlias<StackTrace>{
};
class B : public MakeAlias<A>{
};
class C : public MakeAlias<B>{
    public:
    void hello(){
        cout << "hello from ";
        StackTrace::printStackTrace(this);
        cout << endl;
    }

};
int main(){
    C c;
    c.hello();
}

Everything should be okay, but when I try to compile it g++ ignores the
if(typeid(T)==typeid(StackTrace))return; line and returns the following error:

st.cpp: In member function `void StackTrace::printStackTrace(T*) [with T = StackTrace]':
st.cpp:13:   instantiated from `void StackTrace::printStackTrace(T*) [with T = A]'
st.cpp:13:   instantiated from `void StackTrace::printStackTrace(T*) [with T = B]'
st.cpp:13:   instantiated from `void StackTrace::printStackTrace(T*) [with T = C]'
st.cpp:24:   instantiated from here
st.cpp:12: error: no type named `Base' in `class StackTrace'
st.cpp:13: error: no type named `Base' in `class StackTrace'

It tries to call C::Base::Base::Base::Base /StackTrace::Base/ class, which will never be called during runtime. Even if I put return statement immediately after printStackTrace declaration, the same error is evaluated. Why the scope and the member functions are not checked dynamically and why the compiler ignores the return?


Solution

  • Templates are a purely compile-time construct. They simply instruct the compiler to generate classes or functions, which are then compiled normally (so they must be syntactically valid, except for some special exceptions).

    You can get around this by providing an overload of printStackTrace:

    template <class T>
    void printStackTrace(T *c)
    {
      cout << typeid(T).name() << "." << endl;
      typename T::Base *V;
      printStackTrace(V);
    }
    
    void printStackTrace(StackTrace *c)
    {
      cout << typeid(StackTrace).name() << "." << endl;
    }
    

    Live example