Skip to content

12. Шаблоны функций, методов классов и классов в C . Недостатки шаблонов. Параметры шаблонов. Параметры типы и параметры значения. Шаблоны функций и методов классов. Подстановка параметров в шаблон. Выведение типов параметров шаблона. Явное указание значений типов параметров шаблона при вызове функции. Срезание ссылок и модификатора const.

vla91slav edited this page Jun 15, 2023 · 8 revisions

Шаблоны

В языке Си приходилось создавать подобные функции, работающие с разными типами данных. По существу был copy-past кода под разные типы данных. Можно решать эту проблему с помощью макросов с параметрами, но это крайне опасная вещь. На этапе препроцессирования происходит подстановка макроса в код, на этапе компиляции идет проверка подставленного. Если у нас ошибка в макросе, то определить её крайне сложно.

  • Макрос с параметрами объявляется следующим образом: имя макроса(список параметров) последовательность_символов

Примеры:

#define print(a) printf("%d \n", a)

#define min(a,b) (a < b ? a : b)

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

Понятие шаблона

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

Параметрами шаблона могут быть:

  • Типы. Параметрами типа могут быть простые типы языка си, производные типы языка си и классы.
  • Параметры значений. Параметры значения - только константные параметры целого типа или указатели с внешним связыванием.
  • Синтаксис шаблона в общем виде:
template<[параметры шаблона]>
функция | класс | тип

Про typename и class

Для параметра типа изначально использовалось ключевое слово class, но программисты стали возражать, так как в С++ простые типы изначально не были классами, и разработчик языка добавил еще одно ключевое слово - typename. Разницы между описанием class и typename нет, это сделано для того, чтобы лучше читался код, чтобы было понимание, что это тип.

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

Возможно два варианта создания функции по шаблону:

  • Функция принимает параметры шаблона, например, void freeArray(Type* arr);
  • Явное указание параметров функции, например, Type* initArray(int count);

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

Определение функции представляет из себя то же самое, что объявление, только с телом. В коде параметры шаблона мы используем как типы, и при создании функции по шаблону это имя заменяется конкретным типом.

Пример использования шаблонов функций и типа

// Объявление шаблона функции и имени параметра
template <typename Type>    // Type - имя параметра, который используется в шаблоне 
    Type* initArray(int count); // Параметры шаблонной функции заданы явно

template <typename Type>
void freeArray(Type* arr);

template <typename Type>
Type* inputArray(Type* arr, int q);

template <typename Type>
void outputArray(const Type* arr, int q);

// Шаблона типа
template <typename Type> using Tfunc = int(*)(const Type&, const Type&);

// Объявление шаблона функции с использованием шаблона типа
template <typename Type>
    void sort(Type* arr, int q, Tfunc<Type> cmp);

int compare(const double& d1, const double& d2) { return d1 - d2; }

int main()
{
	const int N = 10;
	double* arr = initArray<double>(N); // Функция генерится с типом double
	cout << "Enter array: ";
	inputArray(arr, N);
	sort(arr, N, compare);
	cout << "Resulting array: ";
	outputArray(arr, N);
	freeArray(arr);
	return 0;
}

template <typename Type>
Type* initArray(int count) { return new Type[count]; }

template <typename Type>
void freeArray(Type* arr) {	delete[]arr; }

template <typename Type>
Type* inputArray(Type* arr, int q)
{
	for (int i = 0; i < q; i++)
		cin >> arr[i];
	return arr;
}

template <typename Type>
void outputArray(const Type* arr, int q)
{
	for (int i = 0; i < q; i++)
		cout << arr[i] << " ";
	cout << endl;
}

template <typename Type>
void sort(Type* arr, int q, Tfunc<Type> cmp)
{
	for (int i = 0; i < q - 1; i++)
		for (int j = i + 1; j < q; j++)
			if (cmp(arr[i], arr[j]) > 0)
				swap(arr[i], arr[j]);
}

Шаблоны классов и методов

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

Определение шаблона класса:

template <typename T>
class A
{
    T elem;
public:
	void f();    
};

Мы определили шаблон класса. Это не класс, это шаблон класса. Методы шаблона класса также являются шаблонами. Впринципе, у нас может быть нешаблонный класс, но с шаблонными методами, то есть в обычном классе можно определить шаблонный метод.

Методы шаблонного класса

Как определяются методы шаблонного класса?

Мы должны указать класс, но у нас класса нет, у нас шаблон (ловушка), поэтому нужно указать параметры шаблона (в данном случае у нас один параметр типа T).

Так же, как и у любой функции, у шаблона метода может быть специализация.

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

// Метод шаблонного класса
template <typename T>
void A<T>::f() {}

// Шаблон со специализацией
template <>
void A<int>::f() {}

Как определить класс? При определении объектов класса, будет по шаблону создаваться класс, поэтому мы должны указать для класса параметры A obj;. По этому шаблону будет создан класс, в котором T заменится на float.

Кроме параметров типа, у нас могут быть параметры значений (объекты с внешним связыванием, целочисленные константы или тип перечисление).

Пример:

template <const char *name>
class A {...};

const char *S = "name";
A<S> x; // ERROR! Нет внешнего связывания

extern char st[] = "Name";
A<st> y; // OK! Есть внешнее связывание. На этапе компиляции
         // идет статическое распределение памяти под массив

Недостатки шаблонов:

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

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

  • Ошибки могут быть сложными: Ошибки, связанные с шаблонами, могут быть сложными и трудными для отладки.

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

Могут быть типами (typename или class) или константными значениями (int, char, bool, и т.д.).

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

Max<int>(1, 2);

Срезание ссылок и модификатора const относится к тому, что при выводе типа параметра шаблона ссылки и const не учитываются. Например:

const int& a = 1;
Max(a, 2);  // Здесь T будет int, а не const int&
Clone this wiki locally