2

My question is, why does the following code not compile:

template<typename t> class c1
{
public:
    typedef int type_name;
    void fn1(type_name x) {}
};

template<typename t> class c2 : public c1<t>
{
public:
    void fn2(type_name x) {}
};

While the following does:

class c1
{
public:
    typedef int type_name;
    void fn1(type_name x) {}
};

class c2 : public c1
{
public:
    void fn2(type_name x) {}
};

As you see, the only difference is that in the first case the classes are templates. Gcc and Clang complain that type_name is not defined in the second class (only in template version). Are typedefs not inherited from parent class? If so, why does it work on non-template version? Is there some exception when using typedefs from templates?

Also, I know that I can make this work with fully-qualified type name, i.e. 'typename c1::type_name'. I just want to know if this is some C++ restriction or compiler bug.

haael
  • 972
  • 2
  • 10
  • 22

1 Answers1

4

They are actually "inherited" (by which I mean they are accessible as members of c2<t>). However, all members (not just types) inherited from a dependent base class (a base class which depends on template parameters) are only looked up inside the class template when the compiler has reason to believe they're dependent.

In other words, when parsing the definition of the template c2, the compiler doesn't know what t will be when instantiating, and so it has no means of guessing what the definition of c1<t> will be (remember that it could be specialised later). So it does not look into c1 at all when looking up a name found in c2. So type_name is not found.

However, if you make the name lookup explicitly depend on template parameters somehow, the compiler will realise it has to postpone the lookup until instantiation. This would happen in these cases:

// Case 1
template<typename t> class c2 : public c1<t>
{
public:
    void fn2(typename c1<t>::type_name x) {}
};

// Case 2
template<typename t> class c2 : public c1<t>
{
public:
    void fn2(typename c2::type_name x) {}
};

In both cases, the thing to the left of :: depends on template parameters, so the compiler will know to postpone lookup until instantiation.

Also note that you need to add the typename keyword; without it, the compiler would have no idea whether the thing to the right of :: is a type or a non-type member. The language standard dictates to treat it as a non-type member in such case, which leads to a syntax error.

However, when c2 is not a template, the definition of all its base classes is fully known when it's parsed, so there is no problem in looking up the name.

You can learn more details about this two-phase lookup in this somewhat related SO question.

Community
  • 1
  • 1
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455