Официально, что такое typename?


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

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

7 92

7 ответов:

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

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

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

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

T::SubType * ptr

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

сообщение в блоге Стэна Липпмана предполагает :-

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

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

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

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

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

typename T::A* a6;

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

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

вот почему у нас есть оба

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

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

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!
    .
    .

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

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

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

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

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

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

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

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

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

два использует:

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

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

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> the reference тип выглядит совершенно иначе, чем для других Ts. По общему признанию, это не меняет вид 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 раньше, когда вы используете вложенный тип шаблона в шаблонах. (Конечно, только если параметр шаблона вашего шаблона используется для этого внутреннего шаблона.)

#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;
}