Відмінність ключових слів "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ОБОВ'ЯЗКОВО використовувати ключове слово, як зазначено вище - воно в цьому випадку не взаємозамінне (зверніть увагу: оскільки C ++ 17 у цьому випадку дозволено обидва ключові слова) .typename

Ви також повинні використовувати 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; };
Георг Фріцше

2
І знову ж таки, param_tце не залежний тип. Залежні типи - це імена, які залежать від параметра шаблону , наприклад foo<param_t>::some_type, не самі параметри шаблону.
Георг Фріцше

2
C ++ 1z пропозицію N4051 дозволить вам використовувати typename, тобто template <typename> typename C.
користувач4112979

4
Станом на GCC 5, G ++ тепер дозволяє вводити ім'я типу в параметрі шаблону шаблону .
Chnossos

95

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

Немає смислової різниці між класом і назвою типу в шаблоні-параметрі.

typenameоднак це можливо в іншому контексті при використанні шаблонів - натякнути компілятору, що ви посилаєтесь на залежний тип. §14.6.2:

Ім'я, яке використовується в оголошенні або визначенні шаблону, і яке залежить від параметра-шаблону, передбачається не називати тип, якщо відповідний пошук імені не знайде ім'я типу або ім'я не кваліфікується за ключовим словом.

Приклад:

typename some_template<T>::some_type

Без typenameкомпілятора взагалі не можна сказати, ви посилаєтесь на тип чи ні.


2
Я розумію правило, але що саме заважає компілятору внутрішньо обробляти some_template <T> як тип? Вибачте, якщо я пропускаю щось очевидне.
батбрат

23

Хоча технічної різниці немає, я бачив два, які використовуються для позначення дещо різних речей.

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

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

Для шаблону, який працюватиме лише там, де Т - справжній клас.

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

Але майте на увазі, що деякі люди користуються цим стилем. Не наказується стандартом і не застосовується компіляторами


15
Я не звинувачую вас у згадці про це, але я вважаю, що ця політика є досить помилковою, оскільки програмісти закінчують час думати про щось, що не має значення ("чи я правильно скористався?"), Щоб вказати на щось, що не має значення " t матерія ("чи існує вбудований тип, який реалізує інтерфейс, необхідний для цього параметра шаблону?"). Якщо використовуються будь-які члени параметра шаблону ( T t; int i = t.toInt();), тоді вам потрібен "справжній клас", і ваш код не складеться, якщо ви постачаєте intдля T...
Стів Джессоп

1
Якщо ви хочете обмежити використання фактичними класами, вам краще додати спеціалізацію, щоб кинути / викликати помилку для некласових типів. Якщо ви хочете обмежити використання лише певними класами, спеціалізуйтеся лише на них. У будь-якому випадку таке стилістичне відмінність є занадто тонким, щоб передати повідомлення.
Potatoswatter

2
Оскільки вони означали те саме, будь ласка, використовуйте лише одне. В іншому випадку це як використання вбудованого рядка {, якщо не вівторок, а потім ви використовуєте наступний рядок {.
Пол Дрейпер

+1 Іноді я сам це роблю ... classмається на увазі, що ви не просто очікуєте "значення", можливо, підтримуєте деяких операторів, копіюєте або переміщуєте конструкцію та / або призначення, але конкретно потрібен тип, який підтримує певну семантику доступу для членів. Найшвидший погляд на декларацію потім задає очікування та відштовхує, наприклад, подача вбудованих типів для classпараметрів, коли це, безумовно, буде помилкою.
Тоні Делрой

Я хотів би зрозуміти, які існують ситуації в реальному світі, коли шаблон буде працювати для будь-якого класу, але не буде працювати з вбудованими типами. У вас є приклад?
lfalin

7
  1. Без різниці
  2. Параметр типу шаблону Container- це сам шаблон з двома параметрами типу.

3
є різниця в цілому.
Хасан Сиєд

Чи могли б бути названі ці два параметри, з якими шаблон є шаблоном? у прикладі вони не мають імен. А також - у цьому прикладі написано "class Container" - може також замість цього написати "Container typename"?
Мат

2
@Mat: так, термін для пошуку - це параметри / аргументи шаблону шаблону . Напр .:template<template<class U> class V> struct C {};
Георг Фріцше

6

Цей фрагмент зроблений із книги книг-букварів c ++. Хоча я впевнений, що це неправильно.

Кожному параметру типу повинен передувати клас ключового слова або ім'я типу:

// 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&);

Для позначення параметра типу шаблону може здатися більш інтуїтивним використання ключового слова, а не класу. Зрештою, ми можемо використовувати вбудовані (некласові) типи як аргумент типу шаблону. Більше того, ім'я типу більш чітко вказує на те, що наступне ім'я - це ім'я типу. Однак ім'я типу було додано до C ++ після того, як шаблони вже широко використовувались; деякі програмісти продовжують використовувати виключно клас

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.