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

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

Какая история на typename?

Ответ 1

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

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

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

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

T::SubType * ptr

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

Ответ 2

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

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

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

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

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

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

typename T::A* a6;

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

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

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

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

Ответ 3

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

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

Ответ 4

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

Например

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

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

Я думаю, что во всех ответах упоминается, что ключевое слово typename используется в двух разных случаях:

а) При объявлении параметра типа шаблона. например

template<class T> class MyClass{};        // these two cases are
template<typename T> class MyNewClass{};  // exactly the same.

Что нет никакой разницы между ними, и они точно так же.

б) Перед использованием вложенного имени зависимого типа для шаблона.

template<class T>
void foo(const T & param)
{
   typename T::NestedType * value; // we should use typename here
}

Если не использовать typename, это приведет к ошибкам синтаксического анализа/компиляции.

Что я хочу добавить ко второму случаю, как упоминалось в книге Скотта Мейерса Effective C++, так это то, что есть исключение использования typename перед вложенным именем зависимого типа, Исключением является то, что если вы используете имя вложенного зависимого типа либо в качестве базового класса, либо в списке инициализации члена, вам не следует использовать там typename:

template<class T>
class D : public B<T>::NestedType               // No need for typename here
{
public:
   D(std::string str) : B<T>::NestedType(str)   // No need for typename here
   {
      typename B<T>::AnotherNestedType * x;     // typename is needed here
   }
}

Примечание: Использование typename для второго случая (т.е. перед вложенным именем зависимого типа) не требуется, поскольку C++ 20.