Шаблон (С++)
В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
Шаблоны — это функция языка программирования C++ , которая позволяет функциям и классам работать с универсальными типами . функции или класса Это позволяет объявлению ссылаться через общую переменную на другой класс (встроенный или недавно объявленный тип данных ) без создания полного объявления для каждого из этих разных классов.
Проще говоря, шаблонный класс или функция будет эквивалентом (до «компиляции») копирования и вставки шаблонного блока кода, где он используется, а затем замены параметра шаблона фактическим. По этой причине классы, использующие шаблонные методы, помещают реализацию в заголовки (файлы *.h), поскольку ни один символ не может быть скомпилирован без предварительного знания типа.
Стандартная библиотека C++ предоставляет множество полезных функций в рамках связанных шаблонов.
Основным источником вдохновения для шаблонов C++ послужили параметризованные модули, предоставляемые языком CLU , и дженерики, предоставляемые Ada . [1]
Технический обзор
[ редактировать ]Существует три типа шаблонов: шаблоны функций , шаблоны классов и, начиная с C++14 , шаблоны переменных . Начиная с C++11 , шаблоны могут быть либо вариативными , либо невариативными; в более ранних версиях C++ они всегда невариативны.
Шаблоны функций
[ редактировать ]Шаблон функции ведет себя как функция, за исключением того, что шаблон может иметь аргументы многих разных типов (см. пример). Другими словами, шаблон функции представляет собой семейство функций. Формат объявления шаблонов функций с параметрами типа:
template<class identifier> declaration;
template<typename identifier> declaration;
Оба выражения имеют одно и то же значение и ведут себя совершенно одинаково. Последняя форма была введена во избежание путаницы. [2] поскольку параметр типа не обязательно должен быть классом до C++20. (Это может быть базовый тип, например int
или double
.)
Например, стандартная библиотека C++ содержит шаблон функции max(x, y)
который возвращает большее из x
и y
. Этот шаблон функции можно определить следующим образом:
template<typename T> T max(T &a, T &b) { return a > b ? a : b; }
Это единственное определение функции работает со многими типами данных. В частности, он работает со всеми типами данных, для которых определен > (оператор «больше»). Использование шаблона функции экономит место в файле исходного кода, а также ограничивает изменения одним описанием функции и упрощает чтение кода.
Однако шаблон не создает объектный код меньшего размера по сравнению с написанием отдельных функций для всех различных типов данных, используемых в конкретной программе. Например, если программа использует как int
и double
версия max()
шаблон функции, показанный выше, компилятор создаст версию объектного кода max()
который действует на int
аргументы и другую версию объектного кода, которая работает с double
аргументы. Вывод компилятора будет идентичен тому, который был бы получен, если бы исходный код содержал две отдельные нешаблонные версии. max()
, один написан для обработки int
и один написан для обработки double
.
Вот как можно использовать шаблон функции:
#include <iostream>
int main() {
// This will call max<int> by implicit argument deduction.
std::cout << max(3, 7) << '\n';
// This will call max<double> by implicit argument deduction.
std::cout << max(3.0, 7.0) << '\n';
// We need to explicitly specify the type of the arguments;
// although std::type_identity could solve this problem...
std::cout << max<double>(3, 7.0) << '\n';
}
В первых двух случаях аргумент шаблона T
автоматически выводится компилятором как int
и double
, соответственно. В третьем случае автоматический вычет max(3, 7.0)
потерпит неудачу, поскольку тип параметров, как правило, должен точно соответствовать аргументам шаблона. Поэтому мы явно создаем экземпляр double
версия с max<double>()
.
Этот шаблон функции может быть создан с использованием любого типа , допускающего копирование , для которого выражение y > x
действителен. Для пользовательских типов это означает, что оператор «больше» ( >
) должен быть перегружен в типе.
Сокращенные шаблоны функций
[ редактировать ]Начиная с C++20 , используя auto
или Concept auto
в любом из параметров объявления функции это объявление становится сокращенным объявлением шаблона функции . [3] Такое объявление объявляет шаблон функции, и к списку параметров шаблона добавляется один придуманный параметр шаблона для каждого заполнителя:
void f1(auto); // same as template<class T> void f1(T)
void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept
void f3(C2 auto...); // same as template<C2... Ts> void f3(Ts...), if C2 is a concept
void f4(C2 auto, ...); // same as template<C2 T> void f4(T...), if C2 is a concept
void f5(const C3 auto*, C4 auto&); // same as template<C3 T, C4 U> void f5(const T*, U&);
Шаблоны классов
[ редактировать ]Шаблон класса предоставляет спецификацию для создания классов на основе параметров. Шаблоны классов обычно используются для реализации контейнеров . Шаблон класса создается путем передачи ему заданного набора типов в качестве аргументов шаблона. [4] Стандартная библиотека C++ содержит множество шаблонов классов, в частности контейнеры, адаптированные из стандартной библиотеки шаблонов , такие как vector
.
Переменные шаблоны
[ редактировать ]В C++14 шаблоны также можно использовать для переменных, как в следующем примере:
template<typename T>
constexpr T pi = T{3.141592653589793238462643383L}; // (Almost) from std::numbers::pi
Параметры шаблона, не относящиеся к типу
[ редактировать ]Хотя шаблонизация типов, как в приведенных выше примерах, является наиболее распространенной формой шаблонизации в C++, шаблонизация значений также возможна. Так, например, класс, объявленный с помощью
template <int K>
class MyClass;
может быть создан с помощью определенного int
.
В качестве примера из реальной жизни, стандартный фиксированного размера массива тип std::array
шаблонизирован как по типу (представляющему тип объекта, который содержит массив), так и по числу, имеющему тип std::size_t
(представляющее количество элементов, содержащихся в массиве). std::array
можно объявить следующим образом:
template<class T, size_t N> struct array;
и массив из шести char
s может быть объявлено:
array<char, 6> myArray;
Специализация шаблона
[ редактировать ]Когда на основе шаблона создается экземпляр функции или класса, компилятор создает специализацию этого шаблона для набора используемых аргументов, и эта специализация называется сгенерированной специализацией.
Явная специализация шаблона
[ редактировать ]Иногда программист может решить реализовать специальную версию функции (или класса) для заданного набора аргументов типа шаблона, что называется явной специализацией. Таким образом, определенные типы шаблонов могут иметь специализированную реализацию, оптимизированную для этого типа, или более содержательную реализацию, чем универсальная реализация.
- Если шаблон класса специализируется по подмножеству своих параметров, это называется частичной специализацией шаблона (шаблоны функций не могут быть частично специализированными).
- Если все параметры специализированы, то это полная специализация .
Явная специализация используется, когда поведение функции или класса для конкретного выбора параметров шаблона должно отклоняться от общего поведения: то есть от кода, сгенерированного основным шаблоном или шаблонами. Например, определение шаблона ниже определяет конкретную реализацию max()
для аргументов типа const char*
:
#include <cstring>
template<>
const char* max(const char* a, const char* b) {
// Normally, the result of a direct comparison
// between two C strings is undefined behaviour;
// using std::strcmp makes defined.
return std::strcmp(a, b) > 0 ? a : b;
}
Вариадические шаблоны
[ редактировать ]В C++11 появились шаблоны с переменным числом аргументов , которые могут принимать переменное количество аргументов, что похоже на функции с переменным числом аргументов, такие как std::printf
.
Псевдонимы шаблонов
[ редактировать ]В C++11 представлены псевдонимы шаблонов, которые действуют как параметризованные определения типов .
Следующий код показывает определение псевдонима шаблона. StrMap
. Это позволяет, например, StrMap<int>
использоваться как сокращение для std::unordered_map<int,std::string>
.
template<typename T> using StrMap = std::unordered_map<T, std::string>;
Общие функции программирования на других языках
[ редактировать ]Первоначально концепция шаблонов не была включена в некоторые языки, например Java и C# 1.0. Использование дженериков в Java имитирует поведение шаблонов, но технически отличается. В C# добавлены дженерики (параметризованные типы) в .NET 2.0. Обобщенные шаблоны в Ada появились раньше шаблонов C++.
Хотя шаблоны C++, дженерики Java и дженерики .NET часто считаются похожими, дженерики лишь имитируют базовое поведение шаблонов C++. [5] Некоторые расширенные функции шаблонов, используемые такими библиотеками, как Boost и STLSoft , а также реализациями STL для метапрограммирования шаблонов (явная или частичная специализация, аргументы шаблона по умолчанию, аргументы, не относящиеся к типу шаблона, аргументы шаблона шаблона,...) недоступны. с дженериками.
В шаблонах C++ случаи во время компиляции исторически выполнялись путем сопоставления шаблонов с аргументами шаблона. Например, базовый класс шаблона в приведенном ниже примере факториала реализуется путем сопоставления 0, а не с помощью проверки неравенства, которая ранее была недоступна. Однако появление в C++11 функций стандартной библиотеки, таких как std::conditional, предоставило другой, более гибкий способ обработки создания экземпляров условного шаблона.
// Induction
template<unsigned N>
struct Factorial {
static constexpr unsigned value = N * Factorial<N - 1>::value;
};
// Base case via template specialization:
template<> struct Factorial<0> {
static constexpr unsigned value = 1;
};
С помощью этих определений можно вычислить, скажем, 6! во время компиляции с использованием выражения Factorial<6>::value
.
Альтернативно, constexpr
в С++11/ consteval
в C++20 можно использовать для вычисления таких значений напрямую с помощью функции во время компиляции. По этой причине метапрограммирование шаблонов сейчас в основном используется для выполнения операций над типами.
См. также
[ редактировать ]- Template metaprogramming
- Metaprogramming
- Мономорфизация
- Общее программирование
- Только заголовок
- Неудачная замена не является ошибкой
- Любопытно повторяющийся шаблон шаблона
- Список библиотек шаблонов C++
Ссылки
[ редактировать ]- ^ Страуструп, Бьярне (8 сентября 2004 г.). «Язык программирования C++» . Stroustrup.com (личная домашняя страница) (3-е, специальное изд.).
- ^ Липпман, Стэн (11 августа 2004 г.). «Почему C++ поддерживает как класс, так и имя типа для параметров типа» . Сеть разработчиков Microsoft (MSDN) .
- ^ «P1141R1 — Еще один подход к ограниченным объявлениям» . Архивировано из оригинала 11 ноября 2018 г. Проверено 11 ноября 2018 г.
- ^ Вандевурде, Дэвид; Джосуттис, Николай (2002). Шаблоны C++: Полное руководство . Эддисон Уэсли . ISBN 978-0-201-73484-3 .
- ^ «Различия между шаблонами C++ и универсальными шаблонами C# (Руководство по программированию на C#)» . 12 марта 2024 г.