Разница ключевых слов "typename" и "class" в шаблонах?

504

Для шаблонов я видел оба объявления:

template < typename T >
template < class T >

Какая разница?

И что именно означают эти ключевые слова в следующем примере (взятом из немецкой статьи в Википедии о шаблонах)?

template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};
Мат
источник

Ответы:

430

typenameи classвзаимозаменяемы в основном случае указания шаблона:

template<class T>
class Foo
{
};

а также

template<typename T>
class Foo
{
};

эквивалентны.

Сказав это, есть конкретные случаи, когда есть разница между typenameиclass .

Первый - в случае зависимых типов. typenameиспользуется для объявления, когда вы ссылаетесь на вложенный тип, который зависит от другого параметра шаблона, такого как typedefв этом примере:

template<typename param_t>
class Foo
{
    typedef typename param_t::baz sub_t;
};

Второе, которое вы на самом деле показываете в своем вопросе, хотя можете не осознавать этого:

template < template < typename, typename > class Container, typename Type >

При указании шаблона шаблона , то classключевое слово должно использоваться , как описано выше , - это не взаимозаменяемы typenameв этом случае (примечание: так как C ++ 17 как ключевые слова разрешены в данном случае) .

Вы также должны использовать classпри явном создании шаблона:

template class Foo<int>;

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

Аарон Клоц
источник
45
Последний из них является частным случаем того факта, что вы должны использовать класс или структуру, а не имя типа, чтобы определить класс. Очевидно, что ни один из ваших первых двух битов кода не может быть заменен template <typename T> typename Foo {};, потому что Foo <T> определенно является классом.
Стив Джессоп
2
std::vector<int>::value_typeэто не зависимый тип, он вам там не нужен typename- он нужен вам только в том случае, если тип зависит от параметра шаблона, скажем,template<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche
2
И опять же, param_tэто не зависимый тип. Зависимые типы - это имена, которые зависят от параметра шаблона , например foo<param_t>::some_type, не сами параметры шаблона.
Георг Фрицше
2
C ++ 1z предложение N4051 позволит вам использовать typename, то есть template <typename> typename C.
user4112979
4
По состоянию GCC 5, G ++ позволяет теперь TypeName в качестве параметра шаблона шаблона .
Chnossos
95

Для именования параметров шаблона typenameи classэквивалентны. §14.1.2:

В параметре шаблона нет семантической разницы между классом и именем типа.

typenameоднако это возможно в другом контексте при использовании шаблонов - намекнуть компилятору, что вы ссылаетесь на зависимый тип. §14.6.2:

Предполагается, что имя, используемое в объявлении или определении шаблона и которое зависит от параметра-шаблона, не будет называть тип, если только применимый поиск имени не найдет имя типа или имя не будет квалифицировано ключевым словом typename.

Пример:

typename some_template<T>::some_type

Без typenameкомпилятора не могу вообще сказать, имеете ли вы в виду тип или нет.

Георг Фрицше
источник
2
Я понимаю правило, но что именно мешает компилятору внутренне рассматривать some_template <T> как тип? Извините, если я упускаю что-то очевидное.
batbrat
23

Хотя технических различий нет, я видел, как эти два понятия обозначают несколько разные вещи.

Для шаблона, который должен принимать любой тип как T, включая встроенные модули (такие как массив)

template<typename T>
class Foo { ... }

Для шаблона, который будет работать только там, где T - реальный класс.

template<class T>
class Foo { ... }

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

Майкл Андерсон
источник
15
Я не виню вас за то, что вы упомянули об этом, но я думаю, что эта политика довольно ошибочна, поскольку программисты в конечном итоге тратят время на размышления о чем-то, что не имеет значения («я использовал правильный?»), Чтобы указать на то, что не не имеет значения («существует ли встроенный тип, который реализует интерфейс, необходимый для этого параметра шаблона?»). Если используются какие-либо члены параметра шаблона ( T t; int i = t.toInt();), то вам нужен «реальный класс», и ваш код не будет компилироваться, если вы предоставите intдля него T...
Стив Джессоп
1
Если вы хотите ограничить использование фактическими классами, вам лучше добавить специализацию, чтобы вызвать / вызвать ошибку для не классовых типов. Если вы хотите ограничить использование конкретными классами, специализируйтесь только на них. В любом случае, такое стилистическое различие слишком тонкое, чтобы донести смысл.
Potatoswatter
2
Поскольку они означают одно и то же, пожалуйста, используйте только один. В противном случае это похоже на использование inline {, если это не вторник, а затем вы используете следующую строку {.
Пол Дрэйпер
+1 Я делаю это сам иногда ... classподразумевает, что вы не просто ожидаете "значение", возможно, поддерживающее некоторые операторы, копируете или перемещаете конструкцию и / или присваивание, но конкретно нуждаетесь в типе, поддерживающем некоторую семантику доступа к члену. Самый быстрый взгляд на объявление затем устанавливает ожидания и препятствует, например, предоставляя встроенные типы для classпараметров, когда это, безусловно, будет ошибкой.
Тони Делрой
Я хотел бы понять, какие существуют реальные ситуации, когда шаблон будет работать для ЛЮБОГО класса, но не будет работать со встроенными типами. У вас есть пример?
Ифалин
7
  1. Нет разницы
  2. Параметр типа шаблона Containerсам по себе является шаблоном с двумя параметрами типа.
Николай Фетиссов
источник
3
в общем разница есть.
Хасан Сайед
Могут ли эти два параметра, с которыми настроен контейнер, также называться? в примере у них нет имен. И также - в этом примере написано «класс Container» - может ли вместо этого также быть написано «typename Container»?
Мат
2
@Mat: да, термин для поиска - это параметры / аргументы шаблона шаблона . Например:template<template<class U> class V> struct C {};
Георг Фрицше
6

Этот фрагмент фрагмента взят из учебника по С ++. Хотя я уверен, что это неправильно.

Каждому параметру типа должен предшествовать ключевое слово class или typename:

// error: must precede U with either typename or class
template <typename T, U> T calc(const T&, const U&);

Эти ключевые слова имеют одинаковое значение и могут использоваться взаимозаменяемо в списке параметров шаблона. Список параметров шаблона может использовать оба ключевых слова:

// ok: no distinction between typename and class in a template parameter list
template <typename T, class U> calc (const T&, const U&);

Может показаться более интуитивно понятным использовать ключевое слово typename, а не class для обозначения параметра типа шаблона. В конце концов, мы можем использовать встроенные (не классовые) типы в качестве аргумента типа шаблона. Более того, typename более четко указывает на то, что следующее имя является именем типа. Однако typename было добавлено в C ++ после того, как шаблоны уже широко используются; некоторые программисты продолжают использовать класс исключительно

KK
источник