Почему в "шаблоне <класс x>" есть "класс"? - PullRequest
4 голосов
/ 08 декабря 2008

Что делает "классная" часть оператора шаблона?

Пример:

template <class T>
class Something
{
    public:
        Something(const T &something);
}

А что еще может туда пойти? Я обычно вижу только «класс».

Ответы [ 3 ]

8 голосов
/ 10 декабря 2008

Чтобы определить параметр шаблона, вам нужно либо сообщить компилятору, что параметр является типом, либо значением.

В начале ...

Если я правильно помню, комитет C ++ не хотел добавлять новое ключевое слово в язык C ++, и поэтому он решил разрешить следующие обозначения:

template<int I>
int getTwice()
{
   return I * 2 ;
}

template<class T>
std::string getType(const T & t)
{
   return typeid(t).name() ;
}

void doSomething()
{
   std::cout << "25 : " << getTwice<25>() << std::endl ;
   std::cout << "5  : " << getTwice<5>() << std::endl ;

   std::cout << "type(25)   : " << getType(25) << std::endl ;
   std::cout << "type(25.5) : " << getType(25.5) << std::endl ;
   std::cout << "type(abc)  : " << getType("abc") << std::endl ;
}

Какие выходные данные в g ++:

25 : 50
5  : 10
type(25)   : i
type(25.5) : d
type(abc)  : A4_c

Первая запись была шаблоном над значением. Итак, у нас есть тип значения в объявлении шаблона:

// "I" is the value, and "int" is the type of the value
template <int I>

Вторая запись была шаблоном над неизвестным типом, и тот факт, что тип не был «известен», был отмечен ключевым словом «класс». Таким образом, в этом контексте «класс» означает «тип».

// "T" is a type... And "class" is the "this-is-a-type" keyword
template <class T> 

Вы заметите, что со второй нотацией, несмотря на ключевое слово class, T может быть ... int или другим встроенным типом. Но тогда лучше иметь это любопытство, чем добавлять новое ключевое слово, вы согласны? ...

Упс ...

Все было хорошо и хорошо, пока кто-то не написал следующий код:

template<class T> // T could be any STL container, for example a vector<char>
void printContainerData(const T & t)
{
   std::cout << "aVector:" ;

   for(T::const_iterator it = t.begin(), itEnd = t.end(); it != itEnd; ++it)
   {
      std::cout << " " << (*it) ;
   }

   std::cout << std::endl ;
}

Конечно, где T :: const_iterator - это тип ... Но тогда это может быть статический член класса типа T и, следовательно, значение. Компилятор может быть довольно запутанным.

В конце концов ...

Решение состояло в том, чтобы сообщить компилятору, что T :: const_iterator действительно тип ... Что приведет к такой записи:

for(class T::const_iterator it = t.begin(), // etc.

Но это считалось невозможным / правильным (класс - это объявления классов, нет?). Поэтому, волоча ноги, они решили, что ключевое слово действительно необходимо, чтобы сообщить компилятору, что символ является типом, а не значением.

«Тип» не рассматривался, я полагаю, потому что создание ключевого слова могло бы привести к поломке большого количества кода. Так что вместо этого использовалось имя типа. С помощью typename мы можем написать:

for(typename T::const_iterator it = t.begin(), // etc.

И для согласованности мы должны использовать:

template <typename T>

Когда T предполагается типом, а не значением. Но по причинам совместимости старые обозначения:

template <class T>

все еще авторизован.

А как насчет ??

eben предложил ответ выше, ответ, который я хотел бы прокомментировать, потому что это довольно интересно:

template<template <class T> class U> // must be "class"
std::string to_string(const U<char>& u)
{
  return std::string(u.begin(),u.end());
}

Я прокомментирую только его «значение» (этот код не может быть использован с контейнерами STL на моем компиляторе g ++, но я думаю, что это не главное): в один момент это накладывает ограничение на U, говоря: « U - это шаблон класса T. Это часть:

template <class T> class U

Что также можно написать:

template <typename T> class U

Поскольку U на самом деле является классом (а не встроенным типом), а T - типом любого типа.

И в следующей строке написано, что U специализируется на char:

std::string to_string(const U<char>& u)

Итак, этот «универсальный код» будет работать только для U, если U объявлен как:

template<typename T>
class U
{
   // Etc.
} ;

И U образуется через символ:

U<char> u ;
// etc.
to_string(u)

Но одна вещь была забыта: нотация, предложенная Эбеном, может быть написана двумя способами:

template<template <class T> class U>
std::string to_string(const U<char>& u)

template<template <typename T> class U>
std::string to_string(const U<char>& u)

Второе ключевое слово "class" само по себе не является ключевым словом "type". Это тип, который является шаблонным классом над T. Таким образом, запутанная запись.

Другой способ написания кода Эбена, снятие ограничений, описанных выше, будет выглядеть примерно так:

template<typename U>
std::string to_string(const U & u)
{
   return std::string(u.begin(),u.end());
}

И пусть компилятор сделает свое волшебство:

std::list<char> myList ;
// etc.
std::cout << to_string(myList) << std:endl ;

(например, код Eben не работал с контейнерами STL, настроенными на "char" в моем компиляторе g ++ ...)

8 голосов
/ 08 декабря 2008

Ключевое слово class означает то же самое, что и ключевое слово typename по большей части. Они оба указывают, что T является типом.

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

template<template <class T> class U> // must be "class"
std::string to_string(const U<char>& u)
{
  return std::string(u.begin(),u.end());
}

Единственное, что вы можете поставить вместо ключевых слов class или typename, - это целочисленный тип. Например:

template<std::size_t max>
class Foo{...};
...
Foo<10> f;

Конкретный пример этого можно найти в стандартной библиотеке std::bitset<N>.

4 голосов
/ 08 декабря 2008

В качестве аргументов шаблона вы также можете использовать следующее:

  • любой цельный тип short, int, long, bool и т. Д.
  • указатели на объекты или функции
  • ссылки на объекты или функции
  • указатели на объекты-члены или функции-члены

Несколько примеров:

template<typename T, int N=4, bool Flag>
class myClass { /*...*/ };

template<typename T, const int& Reference, myClass * Pointer&gt
class someClass { /*...*/ };

template<typename T, int (T::*MemberFunctionPtr)(int, int)>
class anotherClass { /*...*/ };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...