简体   繁体   中英

Keyword “typename” in Templates

Following is the code and quote is from the C++ Templates by Addison Wesley :

template <typename T> 
  class MyClass { 
      typename T::SubType * ptr; 
      … 
  };

Without typename, SubType would be considered a static member. Thus, it would be a concrete variable or object. As a result, the expression T::SubType *ptr would be a multiplication of the static SubType member of class T with ptr.

Now when I compile that code without the keyword 'typename', the error I get is this: type 'T' is not derived from type 'MyClass<T>' .

Is the compiler recognizing 'T'? If not, then shouldn't it be a undefined reference error? If yes, then why is this an error?

Alright here is the complete code:

#include <iostream>
#include <vector>

template <typename T> class MyClass 
{ 
     T::SubType * ptr; 
};

int main ()
{
    return 0;
}

The error I am getting is this:

~/Desktop/notes **g++ templates/programs/trial.cpp**
templates/programs/trial.cpp:6: error: type ‘T’ is not derived from type ‘MyClass<T>’
templates/programs/trial.cpp:6: error: expected ‘;’ before ‘*’ token

Here's another way to get the same error from g++:

class Foo { static const int x = 0;};

template <typename T> class MyClass
{
     Foo::x * ptr;
};

and another:

class Foo { static const int x = 0;};

class MyClass
{
     Foo::x * ptr;
};

But you get a different error for this:

// class Foo { static const int x = 0;};

template <typename T> class MyClass
{
     Foo::x * ptr;
};

So:

  1. Because T is a dependent type, g++ assumes that T::SubType is an object that will be defined by the time second-phase lookup occurs. That's as expected, and is the usual reason that typename is required here.

  2. Even if T::SubType exists and is an object, the code is still bad, just like Foo::x *ptr is bad when Foo::x exists and is an object. I still don't understand what the error message is about - how would it help for Foo to be derived from MyClass ? But the error message is nothing to do with templates.

  3. "Undefined reference" is a linker error. Since this code fails to even compile, you shouldn't expect to see "undefined reference to T" anywhere.

  4. I don't so far see how Foo even could be derived from MyClass . I tried the following to see whether it would give a clue to the meaning of the original message, but it fails because MyClass is an incomplete type, which doesn't tell me anything about what would happen if Foo were derived from MyClass :

class MyClass
{
    class Foo: public MyClass { static const int x = 0;};
     Foo::x * ptr;
};

Comeau gives far more sensible error messages for all these cases - nothing about derived types, just says that T::SubType isn't a type. So explaining g++'s error message is going to take either knowledge or good guesses about g++ internals, and exactly where in the process of trying to parse your class template it finally gives up.

Without typename, SubType would be considered a static member. Thus, it would be a concrete variable or object. As a result, the expression T::SubType *ptr would be a multiplication of the static SubType member of class T with ptr.

This description is incorrect when applied to the example you give. In class bodies there can be no expressions, and no constructs are parsed as a multiplication. However, in the C++03 syntax, there is a construct that looks as follows

struct Base { int a; };
struct Derived : Base {
  Base::a; // access-declaration
};

This construct was deprecated in C++03 but still supported, and means the same as the following

struct Base { int a; };
struct Derived : Base {
  using Base::a; // using-declaration
};

Because you didn't tell the compiler that T::SubType is a type and hence tell the compiler that it should parse it as the type of a pointer declaration, the compiler assumed that T::SubType is the name in an access-declaration. Hence it expected a semicolon directly after it, and hence it expected that T is a base class of MyClass<T> (or that MyClass<T> is a derived class of T ). The error message actually has it backwards:

 if (! UNIQUELY_DERIVED_FROM_P (IDENTIFIER_TYPE_VALUE (cname),
                                   ctype))
      {
        cp_error ("type `%T' is not derived from type `%T'",
                  IDENTIFIER_TYPE_VALUE (cname), ctype);
        ...
      }

While the macro says

 /* Nonzero iff TYPE is uniquely derived from PARENT.  Under MI, PARENT can
    be an ambiguous base class of TYPE, and this macro will be false.  */
 #define UNIQUELY_DERIVED_FROM_P(PARENT, TYPE) ...

Because T::SubType is a dependent name, you need to tell the compiler that SubType is a type (not a static data member) by typing typename keyword.

Read about dependent name here:


EDIT:

As for your question (which you repeated in the comment):

I think what you've posted is not the complete code. So I cannot specifically comment on that. Also, sometimes compilers aren't smart enough to point out the error in template code accurately. So I suggested you to read about dependent name and when & why typename is required. Hopefully, after that you will not have any question!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM