Что значит typename?
Добрый вечер! Есть задача сделать итератор для стека(да, я в курсе что есть другие контейнеры =) ) Подсмотрел у ребят как это сделать, но не могу понять что значит это:
typedef я знаю.
typename — не уверен что понимаю это ключевое слово. Вне template<typename T> как я понял используется для помощи компилятору в определении типа.
а что происходит дальше std::stack<T>::container_type::iterator ? разве у стека есть итератор?
- Вопрос задан более года назад
- 289 просмотров
- Вконтакте
1. typename в данном случае нужен компилятору только как подсказка от разработчика, что последующий идентификатор (т.е. std::stack<T>::container_type::iterator ) — это действительно имя типа. Подсказка нужна потому, что этот typedef вероятно находится также в шаблоне, и мы ещё не знаем, во что конкретно инстанциируется шаблон std::stack (в этом случае говорят, что container_type «is dependent on a template-parameter» — пока не инстанциируем std::stack, не узнаем).
2. Member-тип container_type эквивалентен типу нижележащего контейнера (т.к. std::stack — это адаптер под интерфейс стека, а не реальный контейнер, реальный контейнер для хранения вы выбираете вторым параметром шаблона, по-умолчанию это std::deque).
19.3. Использование ключевого слова typename
Ключевое слово typename может использоваться в списке параметров шаблона для задания параметра типа вместо ключевого слова class . Кроме этого, ключевое слово typename может также использоваться в теле шаблона, чтобы указать компилятору на то, что идентификатор является типом. То есть в теле шаблона ключевое слово typename нужно использовать всегда, когда идентификатор является именем типа и квалифицируется именем па- раметра-типа шаблона или именем шаблона класса. В этом случае имена типов должны иметь следующий вид:
В листинге 19.6 приведен пример использования ключевого слова typename в определении шаблона класса Demo . Если в этом слу-
Глава 19. Шаблоны классов
чае ключевое слово typename опустить, то компилятор не сможет определить смысл идентификатора T::Inner .
Листинг 19.6. Использование ключевого слова typename в определении класса
#include <iostream> using namespace std;
template <class T> struct Demo
typename T::Inner* s; // уточняем, что T::Inner – это тип int what()
Outer::Inner s; s.n = 10;
cout << d.what() << endl; // печатает 10
В заключение этого раздела отметим, что в принципе компилятор может определить смысл любого идентификатора в шаблоне при конкретизации этого шаблона. Поэтому часто при отсутствии ключевого слова typename компилятор все равно верно определяет смысл идентификатора, которому по стандарту должно предшествовать это ключевое слово. Однако такое поведение компилятора не предусмотрено стандартом языка программирова-
Часть II. Язык программирования С++
19.4. Явная специализация шаблона класса
Явной специализацией шаблона класса называется определение шаблона класса для конкретных значений всех параметров этого шаблона. Явная специализация шаблона класса имеет следующий синтаксис:
class|struct|union имя_класса<список_аргументов_шаблона> блок_класса
где имя_класса задает имя шаблона класса, а в угловых скобках указан список аргументов шаблона, для которых выполняется явная специализация этого шаблона класса.
Явная специализация шаблона класса используется в тех же случаях, что и явная специализация шаблонов функций, а именно если:
какой-то тип данных не может быть параметром шаблона класса в силу невозможности такой реализации этого шаблона, которая была бы совместима с другими типами данных;
специальная реализация шаблона класса для какого-то типа данных более эффективна, чем общая реализация шаблона.
В листинге 19.7 приведен пример явной специализации шаблона класса.
Листинг 19.7. Явная специализация шаблона класса
#include <iostream.h> #include <string.h>
template <class T> class Demo
Глава 19. Шаблоны классов
// явная специализация шаблона для строк template<> class Demo<char*>
19.5. Частичная специализация шаблона класса
Шаблон класса, как он был определен в начале этой главы, называется первичным шаблоном класса. Шаблон класса можно специализировать для одного или нескольких параметров, оставляя другие параметры неспециализированными. Такая специализация шаблона класса называется частичной . Определение частичной специализации шаблона класса имеет следующий синтаксис:
class|struct|union имя_класса<список_аргументов_шаблона> блок_класса
где имя_класса должно совпадать с именем первичного шаблона класса, список_параметров_шаблона должен быть подсписком списка параметров первичного шаблона класса, список_аргументов_
Часть II. Язык программирования С++
шаблона должен содержать аргументы частичной специализации шаблона класса.
Например, если объявление первичного шаблона класса имеет вид:
template <class S, class T> class Demo;
то объявления частичных специализаций этого шаблона могут иметь следующий вид:
template <class S, class T> class Demo<*S, T>; template<class S> class Demo<S, *S>; template<class S> class Demo<S, int>;
Определение частичной специализации никак не связано с определением первичного шаблона класса, для которого может быть совершенно другой набор членов, а также собственные определения функций-членов. Содержащиеся в первичном шаблоне класса определения никогда не употребляются для конкретизации членов его частичной специализации.
Отметим, что компилятор может и не конкретизировать шаблон класса по причине неоднозначности его специализаций, которые получены из определения первичного шаблона класса и его частичных специализаций. Для решения этой задачи специализации шаблона класса частично упорядочивают. Частичный порядок на специализациях шаблона класса строится по аналогии с частичным порядком специализаций перегруженных шаблонных функций. То есть параметры шаблона класса рассматриваются как параметры шаблонной функции и дальше применяются правила упорядочивания специализаций шаблонных функций.
При конкретизации шаблона класса компилятор частично упорядочивает специализации шаблона класса, полученные из первичного шаблона и частичных специализаций этого шаблона класса. После чего выбирается наилучшая специализация. Если такая отсутствует, то компилятор выдает ошибку.
В заключение этого раздела заметим, что частичную специализацию шаблонов классов поддерживают не все компиляторы.
Officially, what is typename for?
On occasion I’ve seen some really indecipherable error messages spit out by gcc when using templates. Specifically, I’ve had problems where seemingly correct declarations were causing very strange compile errors that magically went away by prefixing the typename keyword to the beginning of the declaration. (For example, just last week, I was declaring two iterators as members of another templated class and I had to do this).
What’s the story on typename ?
![]()
8 Answers 8
Following is the quote from Josuttis book:
The keyword typename was introduced to specify that the identifier that follows is a type. Consider the following example:
Here, typename is used to clarify that SubType is a type of class T . Thus, ptr is a pointer to the type T::SubType . Without typename , SubType would be considered a static member. Thus
would be a multiplication of value SubType of type T with ptr .
![]()
Stroustrup reused the existing class keyword to specify a type parameter rather than introduce a new keyword that might of course break existing programs. It wasn’t that a new keyword wasn’t considered — just that it wasn’t considered necessary given its potential disruption. And up until the ISO-C++ standard, this was the only way to declare a type parameter.
So basically Stroustrup reused class keyword without introducing a new keyword which is changed afterwards in the standard for the following reasons
As the example given
language grammar misinterprets T::A *aObj; as an arithmetic expression so a new keyword is introduced called typename
it instructs the compiler to treat the subsequent statement as a declaration.
Since the keyword was on the payroll, heck, why not fix the confusion caused by the original decision to reuse the class keyword.
Thats why we have both
You can have a look at this post, it will definitely help you, I just extracted from it as much as I could
![]()
Consider the code
Unfortunately, the compiler is not required to be psychic, and doesn’t know whether T::sometype will end up referring to a type name or a static member of T. So, one uses typename to tell it:
In some situations where you refer to a member of so called dependent type (meaning «dependent on template parameter»), the compiler cannot always unambiguously deduce the semantic meaning of the resultant construct, because it doesn’t know what kind of name that is (i.e. whether it is a name of a type, a name of a data member or name of something else). In cases like that you have to disambiguate the situation by explicitly telling the compiler that the name belongs to a typename defined as a member of that dependent type.
In this example the keyword typename in necessary for the code to compile.
The same thing happens when you want to refer to a template member of dependent type, i.e. to a name that designates a template. You also have to help the compiler by using the keyword template , although it is placed differently
In some cases it might be necessary to use both
(if I got the syntax correctly).
Of course, another role of the keyword typename is to be used in template parameter declarations.
The secret lies in the fact that a template can be specialized for some types. This means it also can define the interface completely different for several types. For example you can write:
One might ask why is this useful and indeed: That really looks useless. But take in mind that for example std::vector<bool> the reference type looks completely different than for other T s. Admittedly it doesn’t change the kind of reference from a type to something different but nevertheless it could happen.
Now what happens if you write your own templates using this test template. Something like this
it seems to be ok for you because you expect that test<T>::ptr is a type. But the compiler doesn’t know and in deed he is even advised by the standard to expect the opposite, test<T>::ptr isn’t a type. To tell the compiler what you expect you have to add a typename before. The correct template looks like this
Bottom line: You have to add typename before whenever you use a nested type of a template in your templates. (Of course only if a template parameter of your template is used for that inner template.)
A Description of the C++ typename keyword
The purpose of this document is to describe the reasoning behind the inclusion of the typename keyword in standard C++, and explain where, when, and how it can and can’t be used.
Note: This page is correct (AFAIK) for C++98/03. The rules have been loosened in C++11.
Table of contents
A secondary use
There is a use of typename that is entirely distinct from the main focus of this discussion. I will present it first because it is easy. It seems to me that someone said «hey, since we’re adding typename anyway, why not make it do this» and people said «that’s a good idea.»
Most older C++ books, when discussing templates, use syntax such as the following:
I know when I was starting to learn templates, at first I was a little thrown by the fact that T was prefaced by class, and yet it was possible to instantiate that template with primitive types such as int. The confusion was very short-lived, but the use of class in that context never seemed to fit entirely right. Fortunately for my sensibilities, it is also possible to use typename:
This means exactly the same thing as the previous instance. The typename and class keywords can be used interchangeably to state that a template parameter is a type variable (as opposed to a non-type template parameter).
I personally like to use typename in this context because I think it’s ever-so-slightly clearer. And maybe not so much «clearer» as just conceptually nicer. (I think that good names for things are very important.) Some C++ programmers share my view, and use typename for templates. (However, later we will see how it’s possible that this decision can hurt readibility.) Some programmers make a distinction between templates that are fully generic (such as the STL containers) and more special purpose ones that can only take certain classes, and use typename for the former category and class for the latter. Others use class exclusively. This is just a style choice.
However, while I use typename in real code, I will stick to class in this document to reduce confusion with the other use of typename.
The real reason for typename
This discussion I think follows fairly closely appendix B from the book C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond by David Abrahams and Aleksey Gurtovoy, though I don’t have it in front of me now. If there are any deficiencies in my discussion of the issues, that book contains the clearest description of them that I’ve seen.
Some definitions
There are two key concepts needed to understand the description of typename, and they are qualified and dependent names.
Qualified and unqualified names
A qualified name is one that specifies a scope. For instance, in the following C++ program, the references to cout and endl are qualified names:
In both cases, the use of cout and endl began with std::.
Had I decided to bring cout and endl into scope with a using declaration or directive*, and used just «cout» by itself, they would have been unqualified names, because they would lack the std::.
(* Remember, a using declaration is like using std::cout;, and actually introduces the name cout into the scope that the using appears in. A using directive is of the form using namespace std; and makes names visible but doesn’t introduce anything. [12/23/07 — I’m not sure this is true. Just a warning.])
Note, however, that if I had brought them into scope with using but still used std::cout, it remains a qualified name. The qualified-ness of a name has nothing to do with what scope it’s used in, what names are visible at that point of the program etc.; it is solely a statement about the name that was used to reference the entity in question. (Also note that there’s nothing special about std, or indeed about namespaces at all. vector<int>::iterator is a nested name as well.)
Dependent and non-dependent names
A dependent name is a name that depends on a template parameter. Suppose we have the following declaration (not legal C++):
The types of the first three declarations are known at the time of the template declaration. However, the types of the second set of three declarations are not known until the point of instantiation, because they depend on the template parameter T.
The names T, vector<T>, and vector<T>::iterator are called dependent names, and the types they name are dependent types. The names used in the first three declarations are called non-dependent names, at the types are non-dependent types.
The final complication in what’s considered dependent is that typedefs transfer the quality of being dependent. For instance:
another_name_for_T is still considered a dependent name despite the type variable T from the template declaration not appearing.
Note: If you’re know some advanced type theory, note that C++’s notion of a dependent name has almost nothing to do with type theorists’ dependent types.
Some other issues of wording
Note that while there is a notion of a dependent type, there is not a notion of a qualified type. A type can be unqualified in one instance, and qualified the next; the qualification is a property of a particular naming of a type, not of the type itself. (Indeed, when a type is first defined, it is always unqualified.)
However, it will be useful to refer to a qualified type; what I mean by this is a qualified name that refers to a type. I will switch back to the more precise wording when I talk about the rules of typename.
The problem
So now we can consider the following example:
What did the programmer intend this bit of code to do? Probably, what the programmer intended was for there to be a class that defined a nested type called iterator:
and for foo to be called with an instantiation of T being that type:
In that case, then line 3 would be a declaration of a variable called iter that would be a pointer to an object of type T::iterator (in the case of ContainsAType, int*, making iter a double-indirection pointer to an int). So far so good.
However, what the programmer didn’t expect is for someone else to come up and declare the following class:
and call foo instantiated with it:
In this case, line 3 becomes a statement that evaluates an expression which is the product of two things: a variable called iter (which may be undeclared or may be a name of a global) and the static variable T::iterator.
Uh oh! The same series of tokens can be parsed in two entirely different ways, and there’s no way to disambiguate them until instantiation. C++ frowns on this situation. Rather than delaying interpretation of the tokens until instantiation, they change the languge:
Before a qualified dependent type, you need typename
To be legal, assuming the programmer intended line 3 as a declaration, they would have to write
Without typename, there is a C++ parsing rule that says that qualified dependent names should be parsed as non-types even if it leads to a syntax error. Thus if there was a variable called iter in scope, the example would be legal; it would just be interpreted as multiplication. Then when the programmer instantiated foo with ContainsAType, there would be an error because you can’t multiply something by a type.
typename states that the name that follows should be treated as a type. Otherwise, names are interpreted to refer to non-types.
This rule even holds if it doesn’t make sense even if it doesn’t make sense to refer to a non-type. For instance, suppose we were to do something more typical and declare an iterator instead of a pointer to an iterator:
Even in this case, typename is required, and omitting it will cause compile error. As another example, typedefs also require use:
The rules
Here, in excruciating detail, are the rules for the use of typename. Unfortunately, due to something which is hopefully not-contagious apparently affecting the standards committee, they are pretty complicated.
- typename is prohibited in each of the following scenarios:
- Outside of a template definition. (Be aware: an explicit template specialization (more commonly called a total specialization, to contrast with partial specializations) is not itself a template, because there are no missing template parameters! Thus typename is always prohibited in a total specialization.)
- Before an unqualified type, like int or my_thingy_t.
- When naming a base class. For example, template <class C> class my_class : C::some_base_type < . >; may not have a typename before C::some_base_type.
- In a constructor initialization list.
Again, these rules are for standard C++98/03. C++11 loosens the restrictions. I will update this page after I figure out what they are.