Ключевое слово "typename" в шаблонах - PullRequest
1 голос
/ 07 апреля 2011

Ниже приведен код и цитата из шаблонов C ++ от Addison Wesley :

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

Без typename SubType будет рассматриваться как статический член.Таким образом, это будет конкретная переменная или объект.В результате выражение T::SubType *ptr будет умножением статического члена SubType класса T на ptr.

Теперь, когда я компилирую этот код без ключевого слова typename, я получаю ошибкуэто: type ‘T’ is not derived from type ‘MyClass<T>’.

Компилятор распознает 'T'?Если нет, то не должно ли это быть неопределенная ссылка ошибка?Если да, то почему это ошибка?

Хорошо, вот полный код:

#include <iostream>
#include <vector>

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

int main ()
{
    return 0;
}

Я получаю следующую ошибку:

~/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

Ответы [ 3 ]

3 голосов
/ 07 апреля 2011

Вот еще один способ получить ту же ошибку из g ++:

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

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

и другой:

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

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

Но вы получите другую ошибку для этого:

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

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

Итак:

  1. Поскольку T является зависимым типом, g ++ предполагает, что T::SubType - это объект, который будет определен во время поиска второй фазы.Это, как и ожидалось, является обычной причиной, по которой typename требуется здесь.

  2. Даже если T::SubType существует и является объектом, код все ещеплохо, как Foo::x *ptr плохо, когда Foo::x существует и является объектом.Я до сих пор не понимаю, о чем это сообщение об ошибке - как бы оно помогло получить Foo из MyClass?Но сообщение об ошибке не имеет ничего общего с шаблонами.

  3. «Неопределенная ссылка» - ошибка компоновщика.Так как этот код не может даже скомпилироваться, вы не должны ожидать появления «неопределенной ссылки на T» где-либо.

  4. Я пока не вижу, как можно получить Fooот MyClass.Я попробовал следующее, чтобы увидеть, даст ли оно ключ к пониманию исходного сообщения, но оно не работает, потому что MyClass является неполным типом, который ничего не говорит мне о том, что произойдет, если Foo получено изMyClass:

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

Comeau дает гораздо более разумные сообщения об ошибках для всех этих случаев - ничего о производных типах, просто говорит, что T::SubType isn 'т тип.Поэтому для объяснения сообщения об ошибке g ++ потребуются либо знания, либо хорошие догадки о внутренностях g ++, и где именно в процессе разбора шаблона вашего класса оно, наконец, сдается.

2 голосов
/ 07 апреля 2011

Без typename SubType будет считаться статическим членом. Таким образом, это будет конкретная переменная или объект. В результате выражение T :: SubType * ptr будет умножением статического члена SubType класса T на ptr.

Это описание неверно применительно к приведенному вами примеру. В телах классов не может быть выражений, и никакие конструкции не анализируются как умножение. Однако в синтаксисе C ++ 03 есть конструкция, которая выглядит следующим образом

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

Эта конструкция устарела в C ++ 03, но все еще поддерживается и означает то же, что и следующая

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

Поскольку вы не сказали компилятору, что T::SubType является типом, и, следовательно, сказали компилятору, что он должен проанализировать его как тип объявления указателя, компилятор предположил, что T::SubType - это имя в доступе. декларация. Следовательно, он ожидал точку с запятой сразу после него, и, следовательно, ожидал, что T является базовым классом из MyClass<T> (или что MyClass<T> является производным классом из T). Сообщение об ошибке фактически имеет его в обратном направлении:

 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);
        ...
      }

Пока макрос говорит

 /* 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) ...
0 голосов
/ 07 апреля 2011

Поскольку T::SubType является зависимым именем, необходимо указать компилятору, что SubType - это тип (не статический элемент данных), введя ключевое слово typename.

Читайте о зависимом имени здесь:


EDIT:

Что касается вашего вопроса (который вы повторили в комментарии):

Я думаю, что вы написали не полный код. Поэтому я не могу конкретно это прокомментировать. Кроме того, иногда компиляторы недостаточно умны, чтобы точно указывать на ошибку в шаблоне кода. Поэтому я предложил вам прочитать о зависимом имени и когда и почему typename требуется. Надеюсь, после этого у вас не возникнет вопросов!

...