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; // неоднозначность