Possible Duplicate:
C++ difference of keywords ‘typename’ and ‘class’ in templates
При определении шаблона функции или шаблона в C++ можно написать следующее:
template <class T> ...
или можно написать это:
template <typename T> ...
Есть ли веская причина предпочесть одно другому?
Я принял самый популярный (и интересный) ответ, но настоящий ответ кажется таким: «Нет, нет веских причин предпочитать один другому».
typename.class.Однако обратите внимание, что до C++ 17 в случае параметров шаблон шаблона требовалось использование class вместо typename. См. user1428839 ответ ниже. (Но этот конкретный случай не вопрос предпочтений, это было требованием языка.)
На самом деле это не дубликат. Спрашивается, когда предпочтительнее. Другой спрашивает о различиях.
Я не понимаю, как это могло быть дубликатом вопроса, заданного более чем через год.
Просто будьте последовательны - это все, что я могу сказать. Я мальчик с типичным именем, но решать тебе.
Если тип, которого я ожидаю, является типом данных по умолчанию, например int, float, double и т. д., То обычно я предпочитаю использовать typename, с другой стороны, если ожидается, что шаблон или есть причина для принятия классов, структур или определяемых пользователем типов, тогда я буду использовать class. Это всего лишь ссылка на себя, поэтому я могу различать цель между двумя шаблонами, даже если синтаксис ключевых слов является взаимозаменяемым в 95% случаев.
этот «возможный дубликат» был задан в 2008 году. «Оригинал» был задан в 2010 году, и они заметили в 2012 году качество.





Насколько я знаю, не имеет значения, какой из них вы используете. В глазах компилятора они эквивалентны. Используйте тот, который вам больше нравится. Обычно я использую класс.
Стэн Липпман говорил об этом здесь. Я подумал, что это было интересно.
Резюме: Страуструп изначально использовал class для определения типов в шаблонах, чтобы избежать введения нового ключевого слова. Некоторые в комитете были обеспокоены тем, что такая перегрузка ключевого слова привела к путанице. Позже комитет ввел новое ключевое слово typename для устранения синтаксической двусмысленности и решил разрешить его также использовать для определения типов шаблонов, чтобы уменьшить путаницу, но для обратной совместимости class сохранил свое перегруженное значение.
И не забудьте прочитать в комментариях, есть ли веская причина использовать «class», а не «typename».
ВОЗРОЖДЕНИЕ! Честно говоря, я не считаю эту статью особенно ясной. в T::A *obj; язык, насколько мне известно, должен анализировать оператор как объявление из-за правила объявления: все, что выглядит как объявление, даже если оно неоднозначно выглядит как что-то еще, должно преобразовываться в объявление [0]. Я также не нашел ясного мнения об этом Бьярне Страуструпе. [0] Язык программирования C++ 3e, Страуструп, Приложение C.13.5, с. 856-858
... более того, Стэн Липпман говорит в статье «Такая дилемма невозможна в рамках дженериков - нет способа безопасно проверить, что любой T содержит A, чтобы среда выполнения могла безопасно создать экземпляр универсального типа». . С каких это пор универсальные типы создаются во время выполнения? Они создаются во время компиляции, и именно по этой причине без ключевого слова export мы должны определять шаблоны в файлах заголовков. И после этого можно было бы ожидать, что компилятор будет знать все о созданном универсальном экземпляре, включая то, что такое T::A, тип или экземпляр.
@wilhelmtell такого правила нет. Правило касается только выражений, которые имеют функциональное приведение в качестве первого подвыражения. В вашем примере, если T::A является типом, конструкция T::A *obj на самом деле неоднозначна в текущем Стандарте (как выяснилось, когда я обсуждал ее с основной группой несколько недель назад). Однако, поскольку все знают, как его следует анализировать, они не заставляют Стандарт говорить, как его следует анализировать. То же самое с f(), когда f является типом (может быть недопустимым вызовом функции и может быть функциональным приведением).
Причина этой путаницы - странное использование нетерминального выражения «id-expression»: некоторые части стандарта считают, что правило «id-expression» не может анализировать имена типов, в то время как другие считают, что это возможно (иногда только злоупотребляя это нетерминал, так как больше использовать нечего). В результате разрешение неоднозначности и даже простое наличие неоднозначности являются более или менее условными, чем те, которые выводятся из Стандарта. Некоторые абзацы думают, что, поскольку T::A - это тип, T::A не может быть id-выражением и, следовательно, T::A *obj не может быть умножением. Но другие абзацы этому противоречат.
Даже после редактирования этот ответ в лучшем случае не впечатляет.
По словам Скотта Майерса, «Эффективный C++» (3-е изд.), Пункт 42 (который, конечно, должен быть окончательным ответом) - разница в «ничто».
Совет - использовать "class", если ожидается, что T всегда будет классом, с "typename", если можно ожидать других типов (int, char *, что угодно). Считайте это подсказкой по использованию.
Мне нравится концепция фактора подсказки. Думаю, я начну этим пользоваться.
"Шаблоны C++ - полное руководство" Дэвид Вандевурде Николай М. Йосуттис. 2.1.1
Но теперь у нас есть static_assert (std :: is_class <T> :: value, «T должен быть классом»);
Это тоже становится волосатым. stackoverflow.com/a/46401548/1043529
Я предпочитаю использовать typename, потому что я не поклонник перегруженных ключевых слов (боже, сколько разных значений у static для разных контекстов?).
Конечно, typename тоже перегружен ....
Верно, но кажется, что он менее перегружен - другие способы использования typename сбивают с толку не из-за перегрузки, а из-за того, что ситуации, когда это требуется, довольно запутаны. Перегрузка других ключевых слов (class или static) кажется активными участниками путаницы.
Я должен согласиться с использованием typename здесь - использование класса, похоже, чрезмерно использует это ключевое слово, особенно в экземплярах template<class X> class Y { ...
Лично мне легче бегло просмотреть код, если он использует typename в качестве параметров шаблона. Если я просто бегаю по экрану в поисках определения или объявления класса, мне приходится смотреть внимательнее каждый раз, когда я вижу class. Между тем, всякий раз, когда я вижу typename, я автоматически думаю: «О, это либо шаблонное объявление, либо одна из тех ненадежных ситуаций». И наоборот, при просмотре шаблонов class может указывать на них, а может и не указывать, но вы знаете, что typename можно использовать только тогда, когда шаблон находится в разработке.
Это вообще не имеет значения, но class делает вид, что T может быть только классом, хотя, конечно, может быть любым типом. Таким образом, typename более точен. С другой стороны, большинство людей используют class, поэтому его, вероятно, легче читать в целом.
Что ж, кто-то может возразить, что возможность использовать "class" делает вид, будто класс может использоваться только для классов, а "typename", таким образом, ограничивается примитивными типами. Так что этот аргумент носит субъективный характер ...
Можно сказать, что каждое имя class - это typename, но не все typename - это имя class. Если рассматривать только это, есть два логических подхода: 1) всегда использовать typename, кроме случаев использования параметров шаблона шаблона в коде до C++ 17, или 2) использовать class, если параметр явно должен быть определяемым пользователем типом. , или typename в других ситуациях. Конечно, реальный мир не такой черно-белый, как этот, и есть также веские аргументы в пользу использования class вместо typename, поэтому лучше использовать тот, который вам или вашей команде удобнее всего.
Я лично считаю, что typename легче анализировать, потому что его значение всегда связано с шаблонами. Другие, с другой стороны, находят class более легким для синтаксического анализа, одна из причин заключается в том, что в typename есть неотъемлемая двусмысленность, поскольку он также является ключевым словом для использования типа, определенного внутри шаблонного типа. Таким образом, это в первую очередь субъективный вопрос, за исключением одного случая, когда язык явно требует одно над другим.
В ответ на Майк Б. я предпочитаю использовать «класс», поскольку в шаблоне «typename» имеет перегруженное значение, а «класс» - нет. Возьмем этот проверенный пример целочисленного типа:
template <class IntegerType>
class smart_integer {
public:
typedef integer_traits<Integer> traits;
IntegerType operator+=(IntegerType value){
typedef typename traits::larger_integer_t larger_t;
larger_t interm = larger_t(myValue) + larger_t(value);
if (interm > traits::max() || interm < traits::min())
throw overflow();
myValue = IntegerType(interm);
}
}
larger_integer_t - это зависимое имя, поэтому перед ним должно стоять 'typename', чтобы синтаксический анализатор мог распознать, что larger_integer_t является типом. класс, с другой стороны, не имеет такого перегруженного значения.
Это ... или я просто ленив в душе. Я набираю «класс» гораздо чаще, чем «имя типа», и поэтому набираю его намного проще. Или это может быть признаком того, что я пишу слишком много объектно-ориентированного кода.
Я не считаю это перегрузкой. В обоих случаях typename делает то же самое: означает, что за ним следует тип, а не переменная.
Но за «typedef» всегда следует тип, так почему же «typename» здесь требуется? Я понимаю, что это требуется в чем-то вроде typename qwert::yuiop * asdfg;, если необходимо сообщить парсеру, что это объявление указателя, а не выражение умножения. Но в typedef такой двусмысленности нет.
Возможно, вы правы, я не уверен, что в моем примере это строго требуется. Ваш пример лучше, как "qwerty :: yuiop * asdfg;" может либо объявлять переменную-указатель, либо вызывать оператор умножения.
это следует читать typedef integer_traits<IntegerType> traits?
Расширение комментария DarenW.
После того, как typename и class не будут считаться очень разными, может быть справедливо строгое их использование. Используйте class, только если это действительно класс, и typename, если это базовый тип, например символ.
Эти типы действительно также принимаются вместо typename
template< charmyc = '/' >
который в этом случае будет даже лучше, чем typename или class.
Подумайте о «понятности» или понятности для других людей. И на самом деле учтите, что стороннее программное обеспечение / скрипты могут попытаться использовать код / информацию, чтобы угадать, что происходит с шаблоном (подумайте о swig).
Просто чистая история. Цитата Стэна Липпмана:
The reason for the two keywords is historical. In the original template specification, 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.
Но лучше использовать typename, а не класс! См. Ссылку для получения дополнительной информации, но подумайте о следующем коде:
template <class T>
class Demonstration {
public:
void method() {
T::A *aObj; // oops ...
};
В дополнение ко всем вышеупомянутым сообщениям, принудительное использование ключевого слова classявляется (до C++ 14 включительно) при работе с параметрами шаблон шаблона, например:
template <template <typename, typename> class Container, typename Type>
class MyContainer: public Container<Type, std::allocator<Type>>
{ /*...*/ };
В этом примере typename Container сгенерировал бы ошибку компилятора, примерно такую:
error: expected 'class' before 'Container'
Похоже, Clang 3.5 и Visual Studio 2015 теперь поддерживают typename N4051 в параметрах шаблона шаблона.
По крайней мере, на данный момент это очень важно и делает этот ответ лучше, чем принятый и второй по величине.
classtypename.typename недопустим для аргументов шаблона шаблона, поэтому для согласованности следует использовать class:
template<template<class> typename MyTemplate, class Bar> class Foo { }; // :(
template<template<class> class MyTemplate, class Bar> class Foo { }; // :)
Обратите внимание, что это несоответствие будет исправлено в C++ 1z.
@MilesRout C++ 1z == C++ 17, верно?
C++ 0x, который стал C++ 1x, был C++ 11. C++ 1y - это C++ 14, текущая версия. C++ 1z, скорее всего, будет C++ 17, если он не упадет до конца этого десятилетия.
Я думаю, что в этом случае было бы оправдано собрать все ответы воедино и принять свой собственный новый ответ вместо того, чтобы помещать ответ в текст вопроса.