vitaut
vitaut

Reputation: 55554

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?

Upvotes: 1

Views: 81

Answers (3)

skypjack
skypjack

Reputation: 50540

You can't do that because of this:

A class is considered defined after the closing brace of its class-specifier has been seen [...]

And a few exceptions, none of which are valid in your case.
In other terms, you must consider your derived class as not fully defined when you try to use it in your base class to access the type C.

Anyway, you can exploit the fact that your derived class is a template class and do this:

template <typename T>
class A;

template <template<typename> class D, typename T>
class A<D<T>> {
private:
    using C = T;
};

Aa you can see, I've not given a definition for the primary template class, thus only the specialization for template classes can be used.
Not sure this is the OP's real case, but it's the case in the example in the question.

Upvotes: 0

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145269

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.

Upvotes: 2

Edward Strange
Edward Strange

Reputation: 40859

You can't because you're in a chicken-egg paradox. The definition of the base requires knowledge of the definition of the derived, which needs the definition of the base to complete. You simply have to come up with an alternative. One example would be to use an external metafunction to communicate the needed type to whoever needs it. Hopefully that's not in any part of the definition of the base's members or you're probably screwed.

Other alternative is to pass T as a second parameter.

Upvotes: 2

Related Questions