c++templatestoken-name-resolution

How to refer to a type defined in a derived class passed as a template argument?


Consider the following example:

template <typename T>
class A {
 private:
  typedef typename T::C C;
};

template <typename T>
class B : public A<B<T>> {
 public:
  typedef T C;
};

int main() {
  B<int> b;
}

Compiling it with GCC gives the following error:

test.cc:5:23: error: no type named 'C' in 'B<int>'
  typedef typename T::C C;
          ~~~~~~~~~~~~^
test.cc:9:18: note: in instantiation of template class 'A<B<int> >' requested here
class B : public A<B<T>> {
                 ^
test.cc:15:10: note: in instantiation of template class 'B<int>' requested here
  B<int> b;
         ^

Why does compiler give an error if B::C is defined and how to fix it?


Solution

  • At this point,

    class B : public A<B<T>> {
    

    … class B is incomplete. Class A can't look inside it.

    The C type definition inside B is accessible from that point inside B, and on. It's also available inside function bodies in B because you can regard a function definition inside the class definition as a shorthand for placing it after the class. But an incomplete class contains nothing as viewed from outside: all that outside code can do is form pointers and references and use the class as template argument.

    template< class C >
    using Ungood = typename C::Number;
    
    struct S
    {
        void foo() { Number x; (void) x; }      // OK
        Ungood<S> uhuh;                         //! Nyet.
    
        using Number = double;
    };
    
    auto main() -> int {}
    

    You can fix your code by changing the design. The most obvious is to pass the type as a separate template argument. But depending on what you're trying to achieve it may be that the inheritance you currently have, isn't really needed or even useful.