8. Шаблоны функций и классов в С . Параметры шаблонов. Специализация шаблонов частичная и полная. Параметры шаблона задаваемые по умолчанию. Шаблоны с переменным числом параметров. Пространства имен. - keykranz/oop_ex GitHub Wiki

Шаблоны функций и классов в С++.

Шаблоны позволяют избавиться от дублирования кода.

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

В С надо либо плодить однотипные функции, что плохо по определению, либо использовать макросы с параметрами, что опасно, так как подстановка идет на этапе компиляции, а проверка - на этапе выполнения. В С++ эта проблема решается шаблонами.

Шаблоны можно определять:

  • функций
  • типов
  • классов
  • методов класса

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

Компилятор встречает вызов шаблона функции (создание класса) - смотрит, может ли он быть использован и если может, то по шаблону создается функция(класс).

Проблемы с шаблонами:

  • Шаблон не есть класс, не есть функция (функция и класс создаются по шаблону). Где определять шаблон? Если мы определяем шаблон в файле реализации (.cpp), объявляем в заголовочном (.h), то из-за раздельной компиляции пока мы не используем шаблон, не будет создаваться функция или класс по шаблону. Поэтому шаблон нужно определять в заголовочном файле.
  • Пока мы чего-то не используем, шаблон не проверяется. Поэтому в шаблонах могут быть заложены ошибки, которые выявляются только при использовании.
  • Мы не можем накладывать никаких ограничений на параметры шаблона.

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

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

  • Типы - простые типы. Производные типы. Классы.
  • Параметры значения - только константные параметры целого типа или указатели с внешним связыванием.
  • Шаблоны...- об этом на репах не пишут, но такой вариант тоже есть...
template <typename Type>
Type sum(Type first, Type second);

Первый пример:
double first = 1.0;
double second = 2.0;

double result = Sum(first, second); // по типам параметров<br>

Второй:
int result = Sum<int>(1, 2);  // явно в угловых скобках<br>

Шаблонный тип: синтаксис

  • В С:
typedef void (*func)(int);
  • В С++:
using func = void (*)(int)

Для using можно определять шаблон:

template <typename Type> using func = int(*)(Type, Type)

Специализация шаблонов частичная и полная.

Специализация - определение функции с конкретными значениями параметров

  • Полная - для всех параметров
  • Неполная - не для всех Специализация тоже является шаблоном
Шаблон:
template <typename Type, template Type2>
void Convert(Type first, Type2 second)
{
    blah-blah ama using Type and Type2 inside
}

Частичная:
template <typename Type, typename Type2 = bool>
void Convert(Type first, Type2 second)
{
    Я вызываюсь при Type2 = bool
}

Полная:
template <typename Type = int, typename Type2 = bool>
int Convert(Type first, Type2 second)
{
    Я вызываюсь при Type = int и Type2 = bool
}

Приоритет для компилятора (в порядке уменьшения):

  • Перегруженная функция
  • Специализации
  • Шаблон
Пример 11. Правило вызова функций.
template <typename Type>
void swap(Type& val1, Type& val2)
{
	Type temp = val1; val1 = val2; val2 = temp;
}

template<>
void swap<float>(float& val1, float& val2)
{
	float temp = val1; val1 = val2; val2 = temp;
}

void swap(float& val1, float& val2)
{
	float temp = val1; val1 = val2; val2 = temp;
}

void swap(int& val1, int& val2)
{
	int temp = val1; val1 = val2; val2 = temp;
}

void main()
{
	const int N = 2;
	int a1[N];
	float a2[N];
	double a3[N];

	swap(a1[0], a1[1]);		// swap(int&, int&)
	swap<int>(a1[0], a1[1]);	// swap<int>(int&, int&)
	swap(a2[0], a2[1]);		// swap(float&, float&)
	swap<float>(a2[0], a2[1]);	// swap<>(float&, float&)
	swap(a3[0], a3[1]);		// swap<double>(double&, double&)
}

Параметры шаблона задаваемые по умолчанию. ?????????

я не уверена, что требуется именно это:

template<class T = void>
struct My_op_functor { /* ... */ };

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

Ставится auto и decltype Пример 12. Определение типа возвращаемого значения для шаблона функции.

template <typename T, typename U>
auto sum(const T& elem1, const U& elem2) -> decltype(elem1 + elem2)
{
	return elem1 + elem2;
}

int main()
{
	auto s = sum(1, 1.2); // будет тип double, так как int + double = double

	cout << "Result: " << s << endl;
}

Методы шаблонного класса - также шаблоны. В обычном классе могут быть шаблонные методы

template <Type>
Class A:
{
Type var;
void f();
}

Метод будет определяться так:

template <Type>
void A<Type>::f()
{
}

Параметры значения:

template <typename Type, const char* string>
Class A:
{}


const char* str = "asd";
A<str> object;  // ошибка. нет внешнего связывния. константная строка может инициализироваться неконстантной

extern char S[] = "asd";
A<S> obj2; // ok, так как есть внешнее связывание

Параметры значения:

template <int b>
Class A:
{}

Под каждый параметр создастся отдельный класс, надо быть аккуратней, они не взаимозаменяемы.


Шаблоны с переменным числом параметров. Пространства имен.

Может быть заменен initializer_list. Нужно ограничивать, как и рекурсию (в примере - функция терминатор с одним параметром) Ну тут добавить нечего, вот пример, вот такой вот синтаксис. Пример 5. Шаблон функции с переменным числом параметров.

template <typename Type>
Type sum(Type value)
{
	return value;
}

template <typename Type, typename ...Args> // сначала три точки при объявлении template
Type sum(Type value, Args... args)         // три точки после типа при использовании
{
	return value + sum(args...);
}

int main()
{
	cout << sum(1, 2, 3, 4, 5) << endl;

	return 0;
}

Пространства имен:

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

namespace myNamespace
{
    class A {...};
}

Доступ происходит извне в пространство имен происходит или через доступ к контексту

myNamespace::A obj();

Или включить всё пространство:

using namespace myNamespace;
A obj();

Имя пространства имен может быть опущено (анонимное пространство имен). Тогда к нему нельзя получить доступ извне Надо стараться избегать злоупотребления.


Прочее

template<typename T1, typename T2=double> // можно задавать параметры по умолчанию
class A {};

template<typename T>
class A<T, T>{}; // специализация (1 вариант)

template<typename T>
class A<T, int> {}; // специализация (2 вариант)

template<typename T1, typename T2>
class A<T1*, T2*>{}; // специализация (3 вариант)
A<int> a0; // шаблон (т.к. нет второго параметра)
A<int, float> a1; // шаблон, ни одна специализация не подходит
A<float, float> a2; // 1
A<float, int> a3; // 2
A<int*, float*> a4; // 3
A<int, int> a5; // неоднозначность
A<int*, int*> a6; // неоднозначность
⚠️ **GitHub.com Fallback** ⚠️