Typename c что это
Алёна C++
программирование для прагматиков
воскресенье, августа 27, 2006
Ключевое слово typename
typename нужен для облегчения задачи компилятору при парсинге загадочных выражений вроде следующего:
template void f()
Ключевое слово typename разрешит это недоразумение, явно сказав компилятору, что речь идет о типе.
template void f()
17 коммент.:
Насколько я знаю, все шаблоны раскрываются не в рантайме, а на этапе компиляции, как define’ы. Почему же тогда компилятор не может сначала одним проходом раскрыть все шаблоны и уже тогда, точно зная, что там на месте T, понять, что такое T::x?
Даже в таких случаях
template
typename LinkedList ::LinkedListElement* LinkedList ::LinkedListElement::GetNext() const <
.
>
где умножение вряд ли возможно, компилятор MSVS 2003 разобраться без typename не может.
Есть еще один замечательный пример:
В обоих случаях компилятор не знает, что такое T::A. Это может быть именем типа, а может быть и именем функции или переменной. Причем компилятор не может гарантировать, что к моменту раскрытия Y, тип T уже будет известен (просто потому, что возможны циклические зависимости между этими типами). В общем без подсказки в виде typename (или мощной эвристики) никак.
И, кстати, стоит упомянуть, что typename рекомендуется использовать вместо class при объявлении шаблона. Например:
Впрочем это ни на что кроме стиля не влияет.
По моему лучше поведение gcc. Хотя оно и требует обязательного использования typename (что соотвествует стандарту), но зато ты уверен что код собирается (да, я ленив и на некоторые простые функции не хочу писать unit test-ы 🙂 )
Вот тут вот интересное обсуждение typename и хороший пример неоднозначности при парсинге a<T::b>c.
Насколько я знаю, все шаблоны раскрываются не в рантайме, а на этапе компиляции, как define’ы. Почему же тогда компилятор не может сначала одним проходом раскрыть все шаблоны и уже тогда, точно зная, что там на месте T, понять, что такое T::x?
На самом деле typename это лишь способ облегчить жизнь производителям компилятора. Но компиляторы уже давно достаточно умные, что бы не требовать подобных подсказок при разрешении неоднозначностей семантики.
Я надеюсь, что в стандарте С++0x все же избавятся от обязательного требования typename при обращении к внутренним типам параметров шаблона.
Ведь этот самый пресловутый typename значительно усложняет жизнь при метапрограммировании.
Когда пишешь сложные мета-алгоритмы, достает писать постоянно typename.
Вот простенький пример.
static const int types_count = boost::mpl::size ::value;
template
struct generate_info_map
<
BOOST_STATIC_ASSERT(n ::type upper_map;
typedef typename at >::type type_value;
typedef long_ type_key;
typedef typename insert >::type type;
>;
Целиком согласен с CyberZX.
Впрочем если до сих пор у всех известных мне компиляторов очень часто выдача строки ошибки компиляции вызвывает у меня нездоровый смех, то надеяться не на что.
PS. Этот язык сгубит высокомерие разработчиков компилятора.
Prokrust:
> очень часто выдача строки ошибки компиляции вызвывает у меня нездоровый смех
Ахха.. И для этого придумали парсер: STL Error Message Decryptor (www.bdsoft.com/tools/stlfilt.html)
Добавлю, что при использовании вложенных шаблонов одного typename для корректного разбора конструкции компилятором недостаточно:
template
struct foo
<
template struct bar <>;
>;
template
void test( typename foo ::bar ) // ошибка
<
>
Компилятору надо обязательно сообщить о том, что вложенный тип тоже является шаблоном:
template
void test(typename foo :: template bar )
<
>
Столкнулся именно с этой проблемой, когда компилировал древнюю ZipIOS++ библиотеку (zipios.sourceforge.net) на GCC 4.
Со старым GCC 2.95.3 всё проходило на ура, а здесь как раз нехватка typename и сказалась %)
template
void func( std::list ::iterator it)
<
>
int main()
<
std::list l;
func ( l.begin());
return 0;
>
Ms Visual C++ 2010 уже ругается:
warning C4346: std::list::iterator: зависимое имя не является типом укажите префикс «typename» для задания типа
error C2146: синтаксическая ошибка: отсутствие «)» перед идентификатором «it»
error C2182: func: недопустимое использование типа «void»
Вроде не были упомянуты шаблонные шаблонные параметры а именно:
// вот так надо
template class Z> class SuperClass
<
// так нельзя заменить
template typename Z> class SuperClass
<
блин, блогспот темплэйты слева сожрал. Ну думаю все поняли, что я имел в виду))
Официально, что такое typename?
иногда я видел некоторые действительно неразборчивые сообщения об ошибках, выплевываемые gcc при использовании шаблонов. В частности, у меня были проблемы, когда, казалось бы, правильные объявления вызывали очень странные ошибки компиляции, которые волшебным образом исчезли, добавив ключевое слово «typename» к началу объявления. (Например, только на прошлой неделе я объявлял двух итераторов членами другого шаблонного класса, и мне пришлось это сделать).
какова история на typename?
7 ответов
Ниже приводится цитата из книги Джосутису:
ключевое слово typename было введено в укажите, что идентификатор, который следующий тип. Рассмотрим следующий пример:
здесь typename используется для уточнения этого Подтип-это тип класса T. таким образом, PTR-указатель на тип T:: Подтип. Без typename, подтип будет считаться статическим членом. Таким образом
будет умножение значение Подтип типа T с ptr.
Страуструп повторно использовать существующий класс ключевое слово для указания параметра типа а не вводить новое ключевое слово это, конечно, может разрушить существующее программы. Это не было новым ключевым словом не рассмотрела-просто что это не считалось необходимым, учитывая его потенциальное нарушение. И до тех пор, пока Стандарт ISO-C++, это был единственный способ объявления типа параметр.
таким образом, в основном Stroustrup повторно использовал ключевое слово класса без введения нового ключевого слова, которое впоследствии изменяется в стандарте по следующим причинам
в качестве примера приведено
грамматика языка неправильно интерпретирует T::A *aObj; в качестве арифметического выражения вводится новое ключевое слово typename
он инструктирует компилятор рассматривать последующий оператор как объявление.
так как ключевое слово было на зарплате, черт возьми, почему!—10—>не исправить путаницу по первоначальному решению для повторного использования ключевое слово class.
вот почему у нас есть оба
вы можете посмотреть на этот пост, это определенно поможет вам, я просто извлек из него столько, сколько мог
к сожалению, компилятор не обязан быть экстрасенсорным и не знает, будет ли T::sometype ссылаться на имя типа или статический член T. Итак, используется typename сказать:
в некоторых ситуациях, когда вы ссылаетесь на члена так называемого зависимая type (что означает «зависимый от параметра шаблона»), компилятор не всегда может однозначно вывести семантическое значение результирующей конструкции, потому что он не знает, что это за имя (т. е. является ли это именем типа, именем члена данных или именем чего-то еще). В таких случаях нужно различать ситуации, явно указывает компилятору, что имя принадлежит к typename, определяемому как член этого зависимого типа.
в этом примере ключевое слово typename in необходимо для компиляции кода.
в некоторых случаях может потребоваться использовать как
(если я правильно синтаксис).
конечно, другая роль ключевого слова typename используется в объявлениях параметров шаблона.
секрет заключается в том, что шаблон может быть специализированным для некоторых типов. Это означает, что он также может определить интерфейс совершенно разные на несколько типов. Например, вы можете написать:
можно спросить, почему это полезно и действительно: это действительно выглядит бесполезным. Но имейте в виду, что, например std::vector на reference тип выглядит совершенно иначе, чем для других T s. По общему признанию, это не меняет вида reference от типа К чему-то разные, но тем не менее это может произойти.
теперь что произойдет, если вы напишете свои собственные шаблоны с помощью этого test шаблон. Что-то вроде этого!—13—>
итог: вы должны добавить typename раньше, когда вы используете вложенный тип шаблона в шаблонах. (Конечно, только если параметр шаблона вашего шаблона используется для этого внутреннего шаблона.)
BestProg
Содержание
Поиск на других ресурсах:
Общая форма обобщенной функции имеет вид:
Например. Объявление функции, которая получает входным параметром переменную обобщенного типа T и возвращает значение обобщенного типа T имеет вид:
2. Примеры обобщенных (шаблонных) функций
Как видно из примера, шаблонная функция
3. Как работает компилятор в случае использования шаблонной функции
Например. Пусть задана функция, которая суммирует два параметра обобщенного типа T :
При следующем вызове такой функции
компилятор сгенерирует следующие реализации функции Add() :
Вывод: компилятор генерирует столько вариантов функции, сколько существует способов ее вызова в программе.
4. Сколько обобщенных типов данных можно определить для шаблонной функции?
5. Пример шаблонной функции, которая содержит два обобщенных типа
6. Что такое явная «перегрузка» шаблонной функции. Каким образом осуществляется явная «перегрузка» шаблонной функции? Пример
7. Что значит «перегрузка» шаблонной функции? Пример
«Перегрузку» шаблонной функции не нужно путать с явной «перегрузкой» шаблонной функции (см. п. 6).
«Перегрузка» шаблонной функции – это использование одного и того же имени для шаблонной функции, но с разным количеством параметров.
8. Каким образом можно применять стандартные и обобщенные параметры в шаблонной функции? Пример
В шаблонной функции разрешается использовать стандартные параметры. Эти параметры объявляются так же, как и для обычной нешаблонной функции.
9. Пример шаблонной функции, реализующей сортировку методом вставки
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).
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:
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
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.
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 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 ::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 ::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.)
Правило 42: Усвойте оба значения ключевого слова typename
Правило 42: Усвойте оба значения ключевого слова typename
Вопрос: какая разница между «class» и «typename» в следующем объявлении шаблона:
template class Widget; // использует “class”
template class Widget; // использует “typename”
Ответ: никакой. Когда в шаблоне объявляется параметр типа, class и type-name означают абсолютно одно и то же. Некоторые программисты предпочитают всегда писать class, потому что это слово короче. Другие (включая меня) предпочитают typename, поскольку оно говорит о том, что параметром не обязательно должен быть тип класса. Некоторые разработчики используют typename, когда допускается любой тип, и резервируют слово class для случаев, когда допускается только тип, определяемый пользователем. Но с точки зрения C++, class и typename в объявлении параметра шаблона означают в точности одно и то же.
Однако не всегда в C++ ключевые слова class и typename эквивалентны. Иногда вы обязаны использовать typename. Чтобы понять – когда именно, поговорим о двух типах имен, на которые можно ссылаться в шаблоне.
Предположим, что у нас есть шаблон функции, принимающей в качестве параметра совместимый с STL-контейнер, содержащий объекты, которые могут быть присвоены величинам типа int. Далее предположим, что эта функция просто печатает значение второго элемента. Это не очень содержательная функция, которая к тому же и реализована по-дурацки. Как я уже говорил, она даже не будет компилироваться, но забудьте об этом на время – все это не так глупо, как кажется:
template // печатает второй
void print2nd(const C& container) // элемент контейнера
if (container.size() >= 2) <
C::const_iterator iter(container.begin()); // получить итератор,
// указывающий на первый
++iter; // сместиться на второй
int value = *iter; // скопировать элемент в int
std::cout // печатает второй элемент контейнера
void print2nd(const C& container) // это некорректный C++!
Выглядит так, будто мы объявили x как локальную переменную – указатель на C::const_iterator. Но это только видимость, поскольку мы «знаем», что C::const_iterator является типом. А что, если в классе C есть статический член данных по имени const_iterator и что, если x будет именем глобальной переменной? В этом случае приведенный код не будет объявлять локальную переменную, а окажется умножением C::const_iterator на x! Звучит невероятно, но это возможно, и авторы синтаксических анализаторов исходного кода на C++ должны позаботиться обо всех возможных вариантах входных данных, даже самых сумасшедших.
Пока о C ничего не известно, мы не можем узнать, является ли C::const_iterator типом или нет, а во время разбора шаблона print2nd компилятор ничего о C не знает. В C++ предусмотрено правило, разрешающее эту неопределенность: если синтаксический анализатор встречает вложенное зависимое имя в шаблоне, он предполагает, что это не имя типа, если только вы не укажете это явно. По умолчанию вложенные зависимые имена не являются типами. Есть исключение из этого правила, о котором я расскажу чуть ниже.
Имея это в виду, посмотрите опять на начало print2nd:
void print2nd(const C& container)
if (container.size() >= 2) <
C::const_iterator iter(container.begin()); // предполагается, что
Теперь должно быть ясно, почему это некорректный C++. Объявление iter имеет смысл только в случае, если C::const_iterator является типом, но мы не сообщили C++ об этом, потому C++ предполагает, что это не так. Чтобы исправить ситуацию, мы должны сообщить C++, что C::const_iterator – это тип. Для этого мы помещаем ключевое слово typename непосредственно перед ним:
template // это корректный С++
void print2nd(const C& container)
if (container.size() >= 2) <
typename C::const_iterator iter(container.begin());
Общее правило просто: всякий раз, когда вы обращаетесь к вложенному зависимому имени в шаблоне, вы должны предварить его словом typename (скоро я опишу исключение).
Слово typename следует использовать для идентификации только вложенных зависимых имен типов; для других имен оно не применяется. Вот пример шаблона функции, который принимает и контейнер, и итератор для этого контейнера:
template // допускается typename (как и “class”)
void f(const C& container, // typename не допускается
typename C::iterator iter); // typename требуется
C не является вложенным зависимым именем типа (оно не вложено внутрь чего-либо, зависимого от параметра шаблона), поэтому его не нужно предварять словом typename при объявлении контейнера, но C::iterator – это вложенное зависимое имя типа, поэтому перед ним следует поставить typename.
Из правила «typename должно предварять вложенные зависимые имена типов» есть исключение: typename не должно предварять вложенные зависимые имена типов в списке базовых классов или в идентификаторе базового класса в списке инициализации членов. Например:
class Derived: public Base ::Nested < // список базовых классов:
public: // typename не допускается
explicit Derived(int x)
:Base ::Nested(x) // идентификатор базового класса
// typename не допускается
typename Base ::Nested temp; // использование вложенного
. // зависимого имени типа не как
. // класса в списке инициализации
>; // членов: typename необходимо
Такая несогласованность несколько раздражает, но по мере приобретения опыта вы перестанете ее замечать.
Рассмотрим еще один пример использования typename, потому нечто подобное можно встретить в реальном коде. Предположим, что мы пишем шаблон функции, которая принимает итератор, и хотим сделать локальную копию – temp – объекта, на который этот итератор указывает. Это можно сделать примерно так:
void workWithIterator(IterT iter)
typename std::iterator_traits ::value_type temp(*iter);
Если вам неприятно даже видеть выражение std::iterator_traits ::value_type, представьте, каково набирать его на клавиатуре. Если вы, как и большинство программистов, считаете, что набрать такое более одного раза немыслимо, определите псевдоним для этого типа посредством typedef. Для имен членов классов-характеристик, к каковым относится value_type, (см. в правиле 47 информацию о классах-характеристиках), принято соглашение, согласно которому имя typedef должно совпадать с именем члена. Таким образом, определение локального typedef обычно выглядит так:
void workWithIterator(IterT iter)
typedef typename std::iterator_traits ::value_type value_type;
Многих программистов соседство typedef и typename поначалу раздражает, но это логическое следствие из правила обращения к вложенным зависимым именам типов. Вы скоро привыкнете. К тому же у вас есть на то веские причины. Сколько раз вы готовы напечатать std::iterator_traits ::value_type?
В качестве заключительного замечания я должен упомянуть, что не все компиляторы настаивают на строгом выполнении правил, касающихся ключевого слова typename. Некоторые принимают код, в котором typename требуется, но пропущено; некоторые принимают код, где typename присутствует, но не допускается; и некоторые (обычно это касается старых компиляторов) отвергают typename даже там, где оно необходимо. Это значит, что взаимосвязи между typename и вложенными зависимыми имен типов могут стать причиной некоторых не очень серьезных ошибок при переносе программ на другую платформу.
Что следует помнить
• В объявлениях параметров шаблона ключевые слова class и typename взаимозаменяемы.
• Используйте typename для идентификации вложенных зависимых имен типов, если они не встречаются в списке базовых классов или в качестве идентификатора базового класса в списках инициализации членов.
Данный текст является ознакомительным фрагментом.
Продолжение на ЛитРес
Читайте также
Вводные слова
Вводные слова Одна из главнейших задач при работе на компьютере – манипулирование данными: создание, модификация, копирование, перемещение и так далее. И тут первое дело – это организация их размещения. Это понятие включает в себя широкий круг частных вопросов – схемы
Исключить слова
Исключить слова «Яндекс» позволяет исключать из выдачи страницы с упоминанием определенных слов. Используйте оператор
. Слева от него укажите, что искать, а справа – какие страницы исключать из поиска. Если вы ищете информацию о Задорнове (но не о министре), введите
Слова признательности
Слова признательности Эта книга вряд ли увидела бы свет без серьезной поддержки со стороны старой доброй The Washington Post. Лучшего места работы для журналиста не сыскать. Мы благодарим председателя совета директоров и генерального директора The Washington Post Company Дона Грэма,
Синхронизация с помощью ключевого слова lock в C#
Синхронизация с помощью ключевого слова lock в C# Первой из возможностей, которую вы можете применить в C# для синхронизации доступа к совместно используемым ресурсам, является использование ключевого слова lock. Это ключевое слово позволяет определить контекст операторов,
1. Пустые значения (Empty-значения)
1. Пустые значения (Empty-значения) Пустое значение – это просто одно из множества возможных значений какого-то вполне определенного типа данных.Перечислим наиболее «естественные», непосредственные пустые значения (т. е. пустые значения, которые мы могли бы выделить
2. Неопределенные значения ( Null-значения)
2. Неопределенные значения (Null-значения) Слово Null используется для обозначения неопределенных значений в базах данных.Чтобы лучше понять, какие значения понимаются под неопределенными, рассмотрим таблицу, являющуюся фрагментом базы данных: Итак, неопределенное
Мастера Слова
Мастера Слова Нанимайте хороших писателейЕсли вы задумываетесь над тем, какого рода специалиста можно еще пригласить на не занятое место, — наймите того, кто лучше других умеет вести документацию. Не важно кто он — дизайнер, программист, специалист по продажам или кто-то
2.3 Ключевые Слова
2.3 Ключевые Слова Следующие идентификаторы зарезервированы для использовния в качестве ключевых слов и не могут использоваться иным образом:asm auto break case char class const continue default delete do double else enum extern float for friend goto if inline int long new operator overload public register return short sizeof static struct switch this typedef union unsigned
Ключевые слова
Ключевые слова Список «key words» – это список ключевых слов, которые Book Designer использует для поиска названий глав в процессе автоматического форматирования книги. Вы можете добавить или исключить ключевые слова из списка при помощи кнопок, расположенных справа от