Использовать "class" или "typename" для параметров шаблона?

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

Я думаю, что в этом случае было бы оправдано собрать все ответы воедино и принять свой собственный новый ответ вместо того, чтобы помещать ответ в текст вопроса.

Catskul 05.10.2009 19:45

На самом деле это не дубликат. Спрашивается, когда предпочтительнее. Другой спрашивает о различиях.

Sebastian Mach 28.02.2013 18:51

Я не понимаю, как это могло быть дубликатом вопроса, заданного более чем через год.

Marc.2377 20.11.2014 08:29

Просто будьте последовательны - это все, что я могу сказать. Я мальчик с типичным именем, но решать тебе.

user4578093 29.06.2015 05:19

Если тип, которого я ожидаю, является типом данных по умолчанию, например int, float, double и т. д., То обычно я предпочитаю использовать typename, с другой стороны, если ожидается, что шаблон или есть причина для принятия классов, структур или определяемых пользователем типов, тогда я буду использовать class. Это всего лишь ссылка на себя, поэтому я могу различать цель между двумя шаблонами, даже если синтаксис ключевых слов является взаимозаменяемым в 95% случаев.

Francis Cugler 27.02.2016 07:08

этот «возможный дубликат» был задан в 2008 году. «Оригинал» был задан в 2010 году, и они заметили в 2012 году качество.

Puddle 19.06.2019 12:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
591
6
277 062
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Насколько я знаю, не имеет значения, какой из них вы используете. В глазах компилятора они эквивалентны. Используйте тот, который вам больше нравится. Обычно я использую класс.

Ответ принят как подходящий

Стэн Липпман говорил об этом здесь. Я подумал, что это было интересно.

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

И не забудьте прочитать в комментариях, есть ли веская причина использовать «class», а не «typename».

Michael Burr 18.10.2008 02:06

ВОЗРОЖДЕНИЕ! Честно говоря, я не считаю эту статью особенно ясной. в T::A *obj; язык, насколько мне известно, должен анализировать оператор как объявление из-за правила объявления: все, что выглядит как объявление, даже если оно неоднозначно выглядит как что-то еще, должно преобразовываться в объявление [0]. Я также не нашел ясного мнения об этом Бьярне Страуструпе. [0] Язык программирования C++ 3e, Страуструп, Приложение C.13.5, с. 856-858

wilhelmtell 09.04.2010 03:29

... более того, Стэн Липпман говорит в статье «Такая дилемма невозможна в рамках дженериков - нет способа безопасно проверить, что любой T содержит A, чтобы среда выполнения могла безопасно создать экземпляр универсального типа». . С каких это пор универсальные типы создаются во время выполнения? Они создаются во время компиляции, и именно по этой причине без ключевого слова export мы должны определять шаблоны в файлах заголовков. И после этого можно было бы ожидать, что компилятор будет знать все о созданном универсальном экземпляре, включая то, что такое T::A, тип или экземпляр.

wilhelmtell 09.04.2010 03:35

@wilhelmtell такого правила нет. Правило касается только выражений, которые имеют функциональное приведение в качестве первого подвыражения. В вашем примере, если T::A является типом, конструкция T::A *obj на самом деле неоднозначна в текущем Стандарте (как выяснилось, когда я обсуждал ее с основной группой несколько недель назад). Однако, поскольку все знают, как его следует анализировать, они не заставляют Стандарт говорить, как его следует анализировать. То же самое с f(), когда f является типом (может быть недопустимым вызовом функции и может быть функциональным приведением).

Johannes Schaub - litb 27.12.2010 12:06

Причина этой путаницы - странное использование нетерминального выражения «id-expression»: некоторые части стандарта считают, что правило «id-expression» не может анализировать имена типов, в то время как другие считают, что это возможно (иногда только злоупотребляя это нетерминал, так как больше использовать нечего). В результате разрешение неоднозначности и даже простое наличие неоднозначности являются более или менее условными, чем те, которые выводятся из Стандарта. Некоторые абзацы думают, что, поскольку T::A - это тип, T::A не может быть id-выражением и, следовательно, T::A *obj не может быть умножением. Но другие абзацы этому противоречат.

Johannes Schaub - litb 27.12.2010 12:12

Даже после редактирования этот ответ в лучшем случае не впечатляет.

underscore_d 14.01.2016 16:47

По словам Скотта Майерса, «Эффективный C++» (3-е изд.), Пункт 42 (который, конечно, должен быть окончательным ответом) - разница в «ничто».

Совет - использовать "class", если ожидается, что T всегда будет классом, с "typename", если можно ожидать других типов (int, char *, что угодно). Считайте это подсказкой по использованию.

Мне нравится концепция фактора подсказки. Думаю, я начну этим пользоваться.

Martin York 18.10.2008 00:44

"Шаблоны C++ - полное руководство" Дэвид Вандевурде Николай М. Йосуттис. 2.1.1

bartolo-otrit 24.09.2011 14:22

Но теперь у нас есть static_assert (std :: is_class <T> :: value, «T должен быть классом»);

paulm 23.02.2016 18:54

Это тоже становится волосатым. stackoverflow.com/a/46401548/1043529

John P 25.12.2017 22:56

Я предпочитаю использовать typename, потому что я не поклонник перегруженных ключевых слов (боже, сколько разных значений у static для разных контекстов?).

Конечно, typename тоже перегружен ....

James Curran 17.10.2008 22:00

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

Michael Burr 17.10.2008 22:18

Я должен согласиться с использованием typename здесь - использование класса, похоже, чрезмерно использует это ключевое слово, особенно в экземплярах template<class X> class Y { ...

Billy ONeal 19.03.2010 22:13

Лично мне легче бегло просмотреть код, если он использует typename в качестве параметров шаблона. Если я просто бегаю по экрану в поисках определения или объявления класса, мне приходится смотреть внимательнее каждый раз, когда я вижу class. Между тем, всякий раз, когда я вижу typename, я автоматически думаю: «О, это либо шаблонное объявление, либо одна из тех ненадежных ситуаций». И наоборот, при просмотре шаблонов class может указывать на них, а может и не указывать, но вы знаете, что typename можно использовать только тогда, когда шаблон находится в разработке.

Justin Time - Reinstate Monica 31.01.2016 00:46

Это вообще не имеет значения, но class делает вид, что T может быть только классом, хотя, конечно, может быть любым типом. Таким образом, typename более точен. С другой стороны, большинство людей используют class, поэтому его, вероятно, легче читать в целом.

Что ж, кто-то может возразить, что возможность использовать "class" делает вид, будто класс может использоваться только для классов, а "typename", таким образом, ограничивается примитивными типами. Так что этот аргумент носит субъективный характер ...

euphrat 06.09.2013 04:08

Можно сказать, что каждое имя class - это typename, но не все typename - это имя class. Если рассматривать только это, есть два логических подхода: 1) всегда использовать typename, кроме случаев использования параметров шаблона шаблона в коде до C++ 17, или 2) использовать class, если параметр явно должен быть определяемым пользователем типом. , или typename в других ситуациях. Конечно, реальный мир не такой черно-белый, как этот, и есть также веские аргументы в пользу использования class вместо typename, поэтому лучше использовать тот, который вам или вашей команде удобнее всего.

Justin Time - Reinstate Monica 29.06.2016 20:59

Я лично считаю, что typename легче анализировать, потому что его значение всегда связано с шаблонами. Другие, с другой стороны, находят class более легким для синтаксического анализа, одна из причин заключается в том, что в typename есть неотъемлемая двусмысленность, поскольку он также является ключевым словом для использования типа, определенного внутри шаблонного типа. Таким образом, это в первую очередь субъективный вопрос, за исключением одного случая, когда язык явно требует одно над другим.

Justin Time - Reinstate Monica 29.06.2016 21:03

В ответ на Майк Б. я предпочитаю использовать «класс», поскольку в шаблоне «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 делает то же самое: означает, что за ним следует тип, а не переменная.

Leon Timmermans 28.10.2008 21:24

Но за «typedef» всегда следует тип, так почему же «typename» здесь требуется? Я понимаю, что это требуется в чем-то вроде typename qwert::yuiop * asdfg;, если необходимо сообщить парсеру, что это объявление указателя, а не выражение умножения. Но в typedef такой двусмысленности нет.

Stewart 16.05.2011 14:59

Возможно, вы правы, я не уверен, что в моем примере это строго требуется. Ваш пример лучше, как "qwerty :: yuiop * asdfg;" может либо объявлять переменную-указатель, либо вызывать оператор умножения.

Aaron 19.05.2011 00:01

это следует читать typedef integer_traits<IntegerType> traits?

obataku 09.05.2016 02:49

Расширение комментария 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 в параметрах шаблона шаблона.

Dwayne Robinson 26.08.2015 06:23

По крайней мере, на данный момент это очень важно и делает этот ответ лучше, чем принятый и второй по величине.

underscore_d 14.01.2016 16:46

Есть разница в является, и вы должны предпочесть classtypename.

Но почему?

typename недопустим для аргументов шаблона шаблона, поэтому для согласованности следует использовать class:

template<template<class> typename MyTemplate, class Bar> class Foo { };    //  :(
template<template<class>    class MyTemplate, class Bar> class Foo { };    //  :)

Обратите внимание, что это несоответствие будет исправлено в C++ 1z.

Miles Rout 24.06.2014 10:34

@MilesRout C++ 1z == C++ 17, верно?

набиячлэвэли 26.03.2015 02:10

C++ 0x, который стал C++ 1x, был C++ 11. C++ 1y - это C++ 14, текущая версия. C++ 1z, скорее всего, будет C++ 17, если он не упадет до конца этого десятилетия.

Miles Rout 26.03.2015 02:59

Другие вопросы по теме