Шаблон (программирование)

Шаблон (программирование)

Шаблоны (англ. template) — средство языка C++, предназначенное для кодирования обобщённых алгоритмов, без привязки к некоторым параметрам (например типам данных, размерам буферов, значениям по умолчанию).

В C++ возможно создание шаблонов функций и классов.

Шаблоны позволяют создавать параметризованные классы и функции. Например, нам нужен какой-то класс:

class SomeClass{
int SomeValue;
int SomeArray[20];
...
}

Для одной конкретной цели мы можем использовать этот класс. Но, вдруг, цель немного изменилась, и нужен еще один класс. Теперь нужно 30 элементов массива SomeArray и вещественный тип SomeValue. Тогда мы можем абстрагироваться от конкретных типов и использовать шаблоны с параметрами. Синтаксис: в начале перед объявлением класса напишем слово template и укажем параметры в угловых скобках. В нашем примере:

template < int ArrayLength, typename SomeValueType > class SomeClass{
SomeValueType SomeValue;
SomeValueType SomeArray[ ArrayLength ];
...
}

Тогда для первой модели пишем:

SomeClass < 20, int > SomeVariable;

для второй:

SomeClass < 30, double > SomeVariable;

Хотя шаблоны предоставляют краткую форму записи участка кода, на самом деле их использование не сокращает исполнимый код, так как для каждого набора параметров компилятор создаёт отдельный экземпляр функции или класса.

Содержание

Шаблоны функций

Синтаксис описания шаблона

Шаблон функции начинается с ключевого слова template, за которым в угловых скобках следует список параметров. Затем следует объявление функции:

template< typename T >
void sort( T array[], int size );  // шаблон sort объявлен, но не определён
 
template< typename T >
void sort( T array[], int size )   // объявление и определение
{
  /* сортировка */
}
 
template< int BufferSize >         // целочисленный параметр
char* read()
{
  char *Buffer = new char[ BufferSize ];
  /* считывание данных */
  return Buffer;
}

Ключевое слово typename появилось сравнительно недавно, поэтому стандарт допускает использование class вместо typename:

template< class T >

Вместо T допустим любой другой идентификатор.

Пример использования

Простейшим примером служит определение минимума из двух величин.

Если a меньше b то вернуть а, иначе - вернуть b

В отсутствие шаблонов программисту приходится писать отдельные функции для каждого используемого типа данных. Хотя многие языки программирования определяют встроенную функцию минимума для элементарных типов (таких как целые и вещественные числа), такая функция может понадобится и для сложных (например «время» или «строка») и очень сложных («игрок» в онлайн-игре) объектов.

Так выглядит шаблон функции определения минимума:

template< typename T >
T min( T a, T b )
{
  return a < b ? a : b;
}

Для вызова этой функции можно просто использовать её имя:

min( 1, 2 );
min( 'a', 'b' );
min( string( "abc" ), string( "cde" ) );

Вызов шаблонной функции

Вообще говоря, для вызова шаблонной функции, необходимо указать значения для всех параметров шаблона. Для этого после имени шаблона указывается список значений в угловых скобках:

int i[5] = { 5, 4, 3, 2, 1 };
sort< int >( i, 5 );
 
char c[] = "бвгда";
sort< char >( c, strlen( c ) );
 
sort< int >( c, 5 );    // ошибка: у sort< int > параметр int[] а не char[]
 
char *ReadString = read< 20 >;
delete [] ReadString;
ReadString = read< 30 >;

Для каждого набора параметров компилятор генерирует новый экземпляр функции. Процесс создания нового экземпляра называется инстанцированием шаблона.

В примере выше компилятор создал две специализации шаблона фунции sort (для типов char и int) и две — шаблона read (для значений BufferSize 20 и 30). Последнее скорее всего расточительно, так как для каждого возможного значения параметра компилятор будет создавать новые и новые экземпляры функций, которые будут отличаться лишь одной константой.

Выведение значений параметров

В некоторых случаях компилятор может сам вывести (логически определить) значение параметра шаблона функции из аргумента функции. Например, при вызове вышеописанной функции sort необязательно указывать параметр шаблона (если он совпадает с типом элементов аргумента-массива):

int i[5] = { 5, 4, 3, 2, 1 };
sort( i, 5 );                   // вызывается sort< int >
 
char c[] = "бвгда";
sort( c, strlen( c ) );         // вызывается sort< char >

Возможно выведение и в более сложных случаях.

В случае использования шаблонов классов с целыми параметрами также возможно выведение этих параметров. Например:

template< int size >
class IntegerArray
{
  int Array[ size ];
  /* ... */
};
template< int size >
void PrintArray( IntegerArray< size > array ) { /* ... */ }
 
IntegerArray<20> ia;
PrintArray( ia );

Правила выведения введены в язык для облегчения использования шаблона и для избежания возможных ошибок, например попытка использования sort< int > для сортировки массива символов.

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

min (0, 'a');
min (7, 7.0);

Ошибки в шаблонах

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

template< class T >
void f( T data )
{
  T *pt = 7;                       // ошибка: инициализация указателя целым числом
  datA = 0;                        // ошибка: неизвестный идентификатор datA
  *pt = data                       // ошибка: нет точки с запятой
}

Ошибки, связанные с использованием конкретных параметров шаблона, нельзя выявить до того, как шаблон использован. Например, шаблон min сам по себе не содержит ошибок, однако использование его с типами, для которых операция '<' не определена, приведёт к ошибке:

struct A
{
  int a;
};
A obj1, obj2;
min( obj1, obj2 );

Если ввести операцию '<' до первого использования шаблона, то ошибка будет устранена. Так проявляется гибкость шаблонов в C++:

inline bool operator< ( const A& a1, const A& a2 ) { return a1.a < a2.a; }
 
min( obj1, obj2 );

Шаблоны классов

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

template< class T >
class List
{
  /* ... */
public:
  void Add( const T& Element );
  bool Find( const T& Element );
  /* ... */
};

Использование шаблонов

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

List<int> li;
List<string> ls;
li.Add( 17 );
ls.Add( "Hello!" );

Технические подробности

Параметры шаблонов

Параметрами шаблонов могут быть: параметры-типы, параметры обычных типов, параметры-шаблоны.

Для параметров любого типа можно указывать значения по умолчанию.

template< class T1,                    // параметр-тип
          typename T2,                 // параметр-тип
          int I,                       // параметр обычного типа
          T1 DefaultValue,             // параметр обычного типа
          template< class > class T3,  // параметр-шаблон
          class Character = char       // параметр по умолчанию
        >

Параметры-шаблоны

Если в шаблоне класса или функции необходимо использовать один и тот же шаблон, но с разными параметрами, то используются параметры-шаблоны. Например:

  template< class Type, template< class > class Container >
  class CrossReferences
  {
    Container< Type > mems;
    Container< Type* > refs;
    /* ... */
  };
 
  CrossReferences< Date, vector > cr1;
  CrossReferences< string, set > cr2;

Нельзя использовать шаблоны функций в качестве параметров-шаблонов.

Правила выведения аргументов шаблона функции

Для параметров, которые являются типами (например параметр T функции sort) возможно выведение, если аргумент функции имеет один из следующих типов:

Тип аргумента Описание
T
const T
volatile T
Сам тип T, возможно с модификаторами const или volatile.
 template< class T >
 T ReturnMe( const T arg ) { return arg; }
 ReturnMe( 7 );
 ReturnMe( 'a' );
T*
T&
T[A]

A — константа
Указатель, ссылка или массив элементов типа T.

Примером может служить шаблон функции sort, рассмотренный выше

Templ<T>
Templ — имя шаблона класса
В качестве аргумента, функция требует конкретную специализацию некоторого шаблона.
 #include <vector>
 template< class T >
 void sort( vector< T > array ) { /* сортировка */ }
 vector<int> i;
 vector<char> c;
 sort( i );
 sort( c );
T (*) (args)
args — некие аргументы
Указатель на функцию, которая возвращает тип T.
 template< class T >
 T* CreateArray( T(*GetValue)(), const int size )
 {
   T *Array = new T[ size ];
   for( int i = 0; i < size; i++ )
     Array[i] = GetValue();
   return Array;
 }
 int GetZero() { return 0; }
 char InputChar()
 {
   char c;
   cin >> c;
   return c;
 }
 int *ArrayOfZeros = CreateArray( GetZero, 20 );
 char *String = CreateArray( InputChar, 40 );
type T::*
T Class::*

type — некий тип
Class — некий класс
Указатель на член класса T произвольного типа.
Указатель на член типа T произвольного класса.
 class MyClass
 {
 public:
   int a;
 };
 template< class T >
 T& IncrementIntegerElement( int T::* Element, T& Object )
 {
   Object.*Element += 1;
   return Object;
 }
 template< class T >
 T IncrementMyClassElement( T MyClass::* Element, MyClass& Object )
 {
   Object.*Element += 1;
   return Object.*Element;
 }
 MyClass Obj;
 int n;
 n = ( IncrementIntegerElement( &MyClass::a, Obj ) ).a;
 n = IncrementMyClassElement( &MyClass::a, Obj );
type (T::*) (args)
T (Class::*) (args)

type — некий тип
Class — некий класс
args — некие аргументы
Указатель на функцию-член класса T.
Указатель на функцию-член некоторого класса, возвращающую тип T.
 class MyClass
 {
 public:
   int a;
   int IncrementA();
 };
 int MyClass::IncrementA() { return ++a; }
 template< class T >
 T& CallIntFunction( int (T::* Function)(), T& Object )
 {
   (Object.*Function)();
   return Object;
 }
 template< class T >
 T CallMyClassFunction( T (MyClass::* Function)(), MyClass& Object )
 {
   return (Object.*Function)();
 }
 MyClass Obj;
 int n;
 n = ( CallIntFunction( &MyClass::IncrementA, Obj ) ).a;
 n = CallMyClassFunction( &MyClass::IncrementA, Obj );

Члены шаблонов классов

Члены шаблона класса являются шаблонами, причём с той же, что и у шаблона класса, параметризацией. В частности это означает, что определение функций-членов следует начинать с заголовка шаблона:

 template< class T >
 class A
 {
   void f( T data );
   void g( void );
 public:
   A();
 };
 template< class T >
 void A<T>::f( T data );
 template< class T >
 void A<T>::g( void );

Внутри области видимости шаблона не нужно повторять спецификатор. Это значит, что например A<T>::A() — это конструктор, хотя можно писать и A<T>::A<T>().

Типы как члены классов

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

 class Container
 {
 public:
   int array[ 15 ];
   typedef int* iterator;
   /* ... */
   iterator begin() { return array; }
 };
 template< class C >
 void f( C& vector )
 {
   C::iterator i = vector.begin();          // ошибка
   typename C::iterator i = vector.begin();
 }

Шаблоны как члены классов

Проблемы возникают и с членами-шаблонами. Если шаблон, который является членом класса, который в свою очередь является параметром шаблона, используется в этом шаблоне и не допускает выведения параметров, то необходимо использовать квалификатор template:

 class A
 {
 /* ... */
 public:
   template< class T > T& ConvertTo();
   template< class T > void ConvertFrom( const T& data );
 };
 template< class T >
 void f( T Container )
 {
   int i1 = Container.template ConvertTo<int>() + 1;
   Container.ConvertFrom( i1 );                          // квалификатор не нужен
 }

Шаблоны в других языках программирования

Язык Ада обладает механизмами, похожими на шаблоны.

Язык D обладает шаблонами, местами более мощными, чем C++.

См. также

Литература

  • Дэвид Вандевурд, Николай М. Джосаттис Шаблоны C++: справочник разработчика = C++ Templates: The Complete Guide. — М.: «Вильямс», 2003. — С. 544. — ISBN 0-201-73484-2

Ссылки


Wikimedia Foundation. 2010.

Игры ⚽ Поможем решить контрольную работу

Полезное


Смотреть что такое "Шаблон (программирование)" в других словарях:

  • Шаблон делегирования — Шаблон проектирования Шаблон делегирования Delegation pattern Описан в Design Patterns Нет В разработке ПО, шаблон делегирования (англ. delegation pattern)  это способ, которым объект внешне выражает некоторое поведение, но в… …   Википедия

  • Шаблон Proxy (шаблон проектирования) — Шаблон Proxy (Заместитель)  Шаблон проектирования. Предоставляет объект, контролирующий доступ, перехватывая все вызовы к нему. Содержание 1 Цель 1.1 Проблема 1.2 Решение 2 Плюсы 3 …   Википедия

  • Шаблон Proxy — (Заместитель)  Шаблон проектирования. Предоставляет объект, контролирующий доступ, перехватывая все вызовы к нему. Содержание 1 Цель 1.1 Проблема 1.2 Решение 2 Плюсы 3 …   Википедия

  • Программирование в ограничениях — Парадигмы программирования Агентно ориентированная Компонентно ориентированная Конкатенативная Декларативная (контрастирует с Императивной) Ограничениями Функциональная Потоком данных Таблично ориентированная (электронные таблицы) Реактивная …   Википедия

  • Шаблон — О шаблонах в Википедии смотрите страницу Википедия:Шаблоны. Шаблон в технике  пластина (лекало, трафарет и т. п.) с вырезами, по контуру которых изготовляются чертежи или изделия либо инструмент для измерения размеров. Шаблон в… …   Википедия

  • Шаблон проектирования — У этого термина существуют и другие значения, см. Паттерн. В разработке программного обеспечения, шаблон проектирования или паттерн (англ. design pattern) повторимая архитектурная конструкция, представляющая собой решение проблемы… …   Википедия

  • Шаблон делегирования (шаблон проектирования) — В разработке ПО, шаблон делегирования (англ. delegation pattern) это способ, которым объект внешне выражает некоторое поведение, но в реальности передаёт ответственность за выполнение этого поведения связанному объекту. Шаблон делегирования… …   Википедия

  • Программирование основанное на прототипах — Прототипное программирование стиль объектно ориентированного программирования, при котором отсутствует понятие класса, а повторное использование (наследование) производится путём клонирования существующего экземпляра объекта прототипа.… …   Википедия

  • Заместитель (шаблон проектирования) — Шаблон Proxy (Заместитель)  Шаблон проектирования. Предоставляет объект, контролирующий доступ, перехватывая все вызовы к нему. Содержание 1 Цель 1.1 Проблема 1.2 Решение 2 Плюсы 3 …   Википедия

  • Событийно-ориентированное программирование — Парадигмы программирования Агентно ориентированная Компонентно ориентированная Конкатенативная Декларативная (контрастирует с Императивной) Ограничениями Функциональная Потоком данных Таблично ориентированная (электронные таблицы) Реактивная …   Википедия


Поделиться ссылкой на выделенное

Прямая ссылка:
Нажмите правой клавишей мыши и выберите «Копировать ссылку»