views:

89

answers:

4

I have the following code and it fails to compile

template < typename T >
class Base
{
public:

    typedef T * TPtr;

    void func()
    {
    }
};

template < typename T >
class Derived : public Base< T >
{
public:
    using Base< T >::TPtr;
    using Base< T >::func;

    TPtr ptr;
};

int main( int c, char *v[] )
{
    Derived< int > d;
    d.func();
}

The compiler issues the following.

t.cpp:16: error: 'TPtr' does not name a type
t.cpp:16: note: (perhaps 'typename Base<T>::TPtr' was intended)

Now I know I could simply do as the compiler is suggesting but I can't understand why the

    using Base< T >::TPtr;

doesn't work.

If I comment out the "TPtr ptr" line then it compiles, proving that the "using Base< T >::func;" statement works.

Any ideas?

A: 

When you write TPtr ptr;

it is seen as Base<T>::TPtr and the compiler will assume that this is a member variable of Base<T> or a method of Base<T> (this is what it will do because Base<T>::TPtr is a 'dependant name' and unfortunately in this case the compiler will assume variable or function instead of type) thus making the declaration invalid.

The keyword typename allow the compiler to know explicitly that TPtr names a type.

Cedric H.
No, that’s wrong: the compiler won’t assume that it’s `static` – in fact, the same declaration works fine for `func` which isn’t `static` either.
Konrad Rudolph
See comment above. I tried using "typename TPtr ptr;" and also tried "using typename Base<T>::TPtr" and they both failed.
ScaryAardvark
@Konrad: I edited, is it correct now ?
Cedric H.
+9  A: 

Base< T >::TPtr is a so-called dependent name so you need to prefix it with typename to make the declaration work.

Additionally, using doesn’t work with typename so you need to use a typedef instead:

typedef typename Base<T>::TPtr TPtr;

The issue is that the compiler can’t decide – without knowing what T is! – whether TPtr in this context names a type or a variable/function. To avoid ambiguities, it always assumes the latter, unless explicitly told otherwise (hence the need for typename).

Konrad Rudolph
I tried "typename TPtr ptr;" and this still failed. "expected nested-name-specifier before TPtr"
ScaryAardvark
@Scary: my mistake. Updated the answer, try again now.
Konrad Rudolph
Yes, I agree this works. However, It kind of defeats the purpose of inheritance if I have to define a new type based on the base type.
ScaryAardvark
+1: thank you Konrad, it been a long time I wished I knew and understood that point before. Now clearly stated and explained by you. I'll use it as soon as I can.
Stephane Rolland
Thanks Konrad :)
ScaryAardvark
I think a more appropriate name is 'dependent name' instead of 'dependent type'.
Chubsdad
+1  A: 

I can't understand why the:
using Base< T >::TPtr;
doesn't work.

Due to possible template specialization, Base< T >::TPtr can be anything: a type name, variable name, function, etc.

With the keyword typename you tell compiler that it can be a type name only.

Here is a decent explanation of what sort of ambiguities it serves to resolve. Scroll down to "The Problem", it covers your case precisely.

Dummy00001
+1 For the link
Cedric H.
A: 

This is part of a defect. While C++03 did provide using typename, it did not provide the necessary rules to say that the name declared by the using declaration is a type-name. It just provided the instrument to say that the name referenced in the using declaration is a type-name. So you can do in fact the following, but with varying success among compilers. Some will make it work, but some won't.

template < typename T >
class Derived : public Base< T >
{
public:
    using typename Base< T >::TPtr;
    using Base< T >::func;
      // up to here it's alright
    TPtr ptr;
      // but this may only work on some compilers
};

This issue has been fixed for C++0x. For C++03, the way to work around it is to use a typedef instead.

Johannes Schaub - litb