Официально, что такое typename? - PullRequest
105 голосов
/ 21 октября 2009

Иногда я видел некоторые действительно не поддающиеся расшифровке сообщения об ошибках, выдаваемые gcc при использовании шаблонов ... В частности, у меня были проблемы, когда, казалось бы, правильные объявления вызывали очень странные ошибки компиляции, которые волшебным образом исчезали с префиксом "typename "ключевое слово в начале объявления ... (Например, только на прошлой неделе я объявил двух итераторов членами другого шаблонного класса, и мне пришлось это сделать) ...

Что за история с typename?

Ответы [ 7 ]

173 голосов
/ 21 октября 2009

Ниже приводится цитата из книги Йосуттиса:

Ключевое слово typename было введено для указать, что идентификатор, который следует тип. Рассмотрим следующий пример:

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

Здесь typename используется для пояснения того, что SubType является типом класса T. Таким образом, ptr - указатель на тип T :: SubType. Без typename, SubType будет рассматриваться как статический член. Таким образом

T::SubType * ptr

будет умножением значения Подтип типа T с ptr.

32 голосов
/ 21 октября 2009

Блог Стэна Липпмана предлагает: -

Страуструп повторно использовал существующий класс ключевое слово для указания параметра типа вместо того, чтобы вводить новое ключевое слово что может, конечно, сломать существующее программы. Это было не то, что новое ключевое слово не считалось - просто это не считалось необходимым, учитывая его потенциальное нарушение. И до Стандарт ISO-C ++, это был единственный способ объявления параметра типа.

Таким образом, в основном Stroustrup повторно использовал ключевое слово класса без введения нового ключевого слова, которое впоследствии было изменено в стандарте по следующим причинам

В качестве примера приведено

template <class T>
class Demonstration {
public:
void method() {
    T::A *aObj; // oops …
     // …
};

грамматика языка неверно интерпретирует T::A *aObj; как арифметическое выражение, поэтому вводится новое ключевое слово с именем typename

typename T::A* a6;

он указывает компилятору обрабатывать последующее утверждение как объявление.

Поскольку ключевое слово было в платежной ведомости, черт, почему не исправить возникшую путаницу первоначальным решением повторно использовать ключевое слово класса.

Вот почему у нас обоих

Вы можете взглянуть на этот пост , он вам определенно поможет, я просто извлек из него столько, сколько смог

13 голосов
/ 21 октября 2009

Рассмотрим код

template<class T> somefunction( T * arg )
{
    T::sometype x; // broken
    .
    .

К сожалению, компилятору не обязательно быть экстрасенсом, и он не знает, будет ли T :: sometype в конце концов ссылаться на имя типа или статический член T. Итак, каждый использует typename, чтобы сказать ему:

template<class T> somefunction( T * arg )
{
    typename T::sometype x; // works!
    .
    .
6 голосов
/ 21 октября 2009

В некоторых ситуациях, когда вы ссылаетесь на член так называемого зависимого типа (что означает «зависит от параметра шаблона»), компилятор не всегда может однозначно вывести семантическое значение результирующей конструкции, поскольку он не не знаю, что это за имя (то есть, является ли оно именем типа, именем члена данных или именем чего-то еще). В подобных случаях вы должны устранить неоднозначность ситуации, явно указав компилятору, что имя принадлежит типу имени, определенному как член этого зависимого типа.

Например

template <class T> struct S {
  typename T::type i;
};

В этом примере необходимо ключевое слово typename, необходимое для компиляции кода.

То же самое происходит, когда вы хотите сослаться на член шаблона зависимого типа, то есть на имя, которое обозначает шаблон. Вы также должны помочь компилятору, используя ключевое слово template, хотя оно размещается по-разному

template <class T> struct S {
  T::template ptr<int> p;
};

В некоторых случаях может потребоваться использовать оба

template <class T> struct S {
  typename T::template ptr<int>::type i;
};

(если я правильно понял синтаксис).

Конечно, другая роль ключевого слова typename должна использоваться в объявлениях параметров шаблона.

5 голосов
/ 21 октября 2009

Секрет заключается в том, что шаблон может быть специализирован для некоторых типов. Это означает, что он также может определять интерфейс, совершенно различный для нескольких типов. Например, вы можете написать:

template<typename T>
struct test {
    typedef T* ptr;
};

template<>         // complete specialization 
struct test<int> { // for the case T is int
    T* ptr;
};

Кто-то может спросить, почему это полезно и действительно: это действительно выглядит бесполезным. Но имейте в виду, что, например, std::vector<bool> тип reference выглядит совершенно иначе, чем для других T s. По общему признанию это не меняет тип reference с типа на что-то другое, но, тем не менее, это может произойти.

Теперь, что произойдет, если вы напишите свои собственные шаблоны, используя этот test шаблон. Как то так

template<typename T>
void print(T& x) {
    test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

вам кажется, что это нормально, потому что вы ожидаете , что test<T>::ptr является типом. Но компилятор не знает, и на самом деле стандарт даже советует ожидать обратного, test<T>::ptr не является типом. Чтобы сообщить компилятору, что вы ожидаете, вы должны добавить typename перед этим. Правильный шаблон выглядит так

template<typename T>
void print(T& x) {
    typename test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

Итог: вы должны добавлять typename раньше, когда вы используете вложенный тип шаблона в ваших шаблонах. (Конечно, только если для этого внутреннего шаблона используется параметр шаблона вашего шаблона.)

4 голосов
/ 21 октября 2009

Два использования:

  1. в качестве ключевого слова аргумента шаблона (вместо 'class')
  2. Ключевое слово typename сообщает компилятору, что идентификатор является типом (а не статической переменной-членом)
template <typename T> class X  // [1]
{
    typename T::Y _member;  // [2] 
}
2 голосов
/ 27 июня 2016
#include <iostream>

class A {
public:
    typedef int my_t;
};

template <class T>
class B {
public:
    // T::my_t *ptr; // It will produce compilation error
    typename T::my_t *ptr; // It will output 5
};

int main() {
    B<A> b;
    int my_int = 5;
    b.ptr = &my_int;
    std::cout << *b.ptr;
    std::cin.ignore();
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...