Официально, что такое typename?
иногда я видел некоторые действительно неразборчивые сообщения об ошибках, выплевываемые gcc при использовании шаблонов... В частности, у меня были проблемы, когда, казалось бы, правильные объявления вызывали очень странные ошибки компиляции, которые волшебным образом исчезли, добавив ключевое слово "typename" к началу объявления... (Например, только на прошлой неделе я объявлял двух итераторов членами другого шаблонного класса, и мне пришлось это сделать)...
какова история на typename?
7 ответов
Ниже приводится цитата из книги Джосутису:
ключевое слово typename было введено в укажите, что идентификатор, который следующий тип. Рассмотрим следующий пример:
template <class T> Class MyClass { typename T::SubType * ptr; ... };
здесь typename используется для уточнения этого Подтип-это тип класса T. таким образом, PTR-указатель на тип T:: Подтип. Без typename, подтип будет считаться статическим членом. Таким образом
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;
он инструктирует компилятор рассматривать последующий оператор как объявление.
так как ключевое слово было на зарплате, черт возьми, почему!--10-->не исправить путаницу по первоначальному решению для повторного использования ключевое слово 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 (что означает "зависимый от параметра шаблона"), компилятор не всегда может однозначно вывести семантическое значение результирующей конструкции, потому что он не знает, что это за имя (т. е. является ли это именем типа, именем члена данных или именем чего-то еще). В таких случаях нужно различать ситуации, явно указывает компилятору, что имя принадлежит к typename, определяемому как член этого зависимого типа.
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
используется в объявлениях параметров шаблона.
два использует:
- в качестве ключевого слова аргумента шаблона (вместо "class")
- ключевое слово 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>
на reference
тип выглядит совершенно иначе, чем для других T
s. По общему признанию, это не меняет вида reference
от типа К чему-то разные, но тем не менее это может произойти.
теперь что произойдет, если вы напишете свои собственные шаблоны с помощью этого test
шаблон. Что-то вроде этого!--13-->
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;
}