Member templates
Объявления шаблонов ( class , function , и variables (since C++14) ) могут находиться внутри member specification любого класса, структуры или объединения, которые не являются local classes .
#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; } // error }; |
(начиная с C++14) |
Шаблоны переменных-членовОбъявление шаблона переменной может находиться в области видимости класса, в этом случае оно объявляет шаблон статического члена данных. См. шаблоны переменных для подробностей. |
(начиная с C++14) |
Отчёты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 1878 | C++14 | operator auto был технически разрешен | operator auto запрещен |