Шаблоны элементы
Объявления шаблонов (классов, функций и переменных (начиная с C++14)) могут появляются внутри спецификации элемента любого класса, структуры или объединения, которые не являются локальным классом.
#include <algorithm> #include <iostream> #include <string> #include <vector> struct Printer { // общий функтор std::ostream& os; Printer(std::ostream& os) : os(os) {} template<typename T> void operator()(const T& obj) { os << obj << ' '; } // шаблон элемента }; int main() { std::vector<int> v = {1,2,3}; std::for_each(v.begin(), v.end(), Printer(std::cout)); std::string s {"abc"}; std::ranges::for_each(s, Printer(std::cout)); }
Вывод:
1 2 3 a b c
Частичные специализации шаблона элемента могут появляться как в области видимости класса, так и в области видимости охватывающего пространства имён. Явные специализации могут появиться в любой области видимости, в которой может появиться основной шаблон.
struct A { template<class T> struct B; // основной шаблон элемента template<class T> struct B<T*> { }; // OK: частичная специализация // template<> struct B<int*> { }; // OK через CWG 727: полная специализация }; template<> struct A::B<int*> { }; // OK template<class T> struct A::B<T&> { }; // OK
Если объявление включающего класса, в свою очередь, является шаблоном класса, когда шаблон элемента определён вне тела класса, он принимает два набора параметров шаблона: один для включающего класса, а другой для самого себя:
template<typename T1> struct string { // элемент шаблон функции template<typename T2> int compare(const T2&); // конструкторы тоже могут быть шаблонами template<typename T2> string(const std::basic_string<T2>& s) { /*...*/ } }; // вне определения класса string<T1>::compare<T2> template<typename T1> // для включающего шаблона класса template<typename T2> // для шаблона элемента int string<T1>::compare(const T2& s) { /* ... */ }
Содержание |
[править] Шаблоны функций-элементов
Деструкторы и конструкторы копирования не могут быть шаблонами. Если объявлен конструктор шаблона, экземпляр которого может быть создан с помощью сигнатуры типа конструктора копирования, вместо него используется неявно объявленный конструктор копирования.
Шаблон функции-элемента не может быть виртуальным, а шаблон функции-элемента в производном классе не может переопределять виртуальную функцию-элемент из базового класса.
class Base { virtual void f(int); }; struct Derived : Base { // этот шаблон элемента не переопределяет Base::f template <class T> void f(T); // переопределение элемента не шаблона может вызвать шаблон: void f(int i) override { f<>(i); } };
Можно объявить нешаблонную функцию-элемент и шаблонную функцию-элемент с одним и тем же именем. В случае конфликта (когда некоторая специализация шаблона точно соответствует сигнатуре функции, не являющейся шаблоном), использование этого имени и типа относится к элементу, не являющемуся шаблоном, если не указан явный список аргументов шаблона.
template<typename T> struct A { void f(int); // элемент не шаблон template<typename T2> void f(T2); // шаблон элемента }; //определение элемента шаблона template<typename T> template<typename T2> void A<T>::f(T2) { // некоторый код } int main() { A<char> ac; ac.f('c'); // вызывает функцию шаблон A<char>::f<char>(char) ac.f(1); // вызывает функцию не шаблон A<char>::f(int) ac.f<>(1); // вызывает функцию шаблон A<char>::f<int>(int) }
Внеклассовое определение шаблона функции-элемента должно быть эквивалентно объявлению внутри класса (смотрите определение эквивалентности в перегрузке шаблона функции), в противном случае это считается перегрузкой.
struct X { template<class T> T good(T n); template<class T> T bad(T n); }; template<class T> struct identity { using type = T; }; // OK: эквивалентное объявление template<class V> V X::good(V n) { return n; } // Ошибка: не эквивалентно ни одному из объявлений внутри X template<class T> T X::bad(typename identity<T>::type n) { return n; }
[править] Шаблоны функций преобразования
Определяемая пользователем функция преобразования может быть шаблоном.
struct A { template<typename T> operator T*(); // преобразование в указатель любого типа }; // внеклассовое определение template<typename T> A::operator T*() {return nullptr;} // явная специализация для char* template<> A::operator char*() {return nullptr;} // явное создание экземпляра template A::operator void*(); int main() { A a; int* ip = a.operator int*(); // явный вызов A::operator int*() }
Во время разрешения перегрузки, специализации шаблонов функций преобразования не находятся поиском по имени. Вместо этого учитываются все видимые шаблоны функций преобразования, и каждая специализация, созданная с помощью вывода аргумента шаблона (которая имеет специальные правила для шаблонов функций преобразования), используется, как если бы она была найдена путём поиска по имени.
Using-объявления в производных классах не могут ссылаться на специализации шаблонов функций преобразования из базовых классов.
Пользовательский шаблон функции преобразования не может иметь выведенный тип возвращаемого значения: struct S { operator auto() const { return 10; } // OK template<class T> operator auto() const { return 42; } // ошибка }; |
(начиная с C++14) |
Шаблоны переменных-элементовОбъявление шаблона переменной может появиться в области видимости класса, и в этом случае оно объявляет шаблон статического элемента данных. Подробнее смотрите шаблон переменной. |
(начиная с C++14) |
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 1878 | C++14 | operator auto был технически разрешённым | operator auto запрещён |