Шаблоны членов
Объявления шаблонов (класса, функции, и переменных(с C++14)) могут появляться внутри спецификации члена любого класса, структуры или объединения, которые не являются локальными классами.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
struct Printer
{
// generic functor
std::ostream& os;
Printer(std::ostream& os) : os(os) {}
template<typename T>
void operator()(const T& obj) { os << obj << ' '; } // member template
};
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; // primary member template
template<class T> struct B<T*> {}; // OK: partial specialization
// template<> struct B<int*> {}; // OK via CWG 727: full specialization
};
template<> struct A::B<int*> {}; // OK
template<class T> struct A::B<T&> {}; // OKЕсли объявление окружающего класса, в свою очередь, является шаблоном класса, при определении шаблона члена за пределами тела класса, он принимает два набора параметров шаблона: один для окружающего класса и другой — для самого себя:
template<typename T1>
struct string
{
// member template function
template<typename T2>
int compare(const T2&);
// constructors can be templates too
template<typename T2>
string(const std::basic_string<T2>& s) { /*...*/ }
};
// out of class definition of string<T1>::compare<T2>
template<typename T1> // for the enclosing class template
template<typename T2> // for the member template
int string<T1>::compare(const T2& s) { /* ... */ }Шаблоны функций-членов
Деструкторы и копирующие конструкторы не могут быть шаблонами. Если объявлен шаблон конструктора, который можно было бы инстанциировать с типом подписи копирующего конструктора, используется неявный копирующий конструктор вместо него.
Шаблон функции-члена не может быть виртуальным, и шаблон функции-члена в производном классе не может переопределять виртуальную функцию-член базового класса.
class Base
{
virtual void f(int);
};
struct Derived : Base
{
// this member template does not override Base::f
template<class T> void f(T);
// non-template member override can call the template:
void f(int i) override
{
f<>(i);
}
};Может быть объявлена как функция-член без шаблона, так и функция-член-шаблон с тем же именем. В случае конфликта (когда какая-то специализация шаблона точно соответствует сигнатуре функции без шаблона), использование этого имени и типа относится к функции-члену без шаблона, если не указан явный список аргументов шаблона.
template<typename T>
struct A
{
void f(int); // non-template member
template<typename T2>
void f(T2); // member template
};
// template member definition
template<typename T>
template<typename T2>
void A<T>::f(T2)
{
// some code
}
int main()
{
A<char> ac;
ac.f('c'); // calls template function A<char>::f<char>(char)
ac.f(1); // calls non-template function A<char>::f(int)
ac.f<>(1); // calls template function 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: equivalent declaration
template<class V>
V X::good(V n) { return n; }
// Error: not equivalent to any of the declarations inside X
template<class T>
T X::bad(typename identity<T>::type n) { return n; }Шаблоны функций преобразования
Пользовательская функция преобразования может быть шаблоном.
struct A
{
template<typename T>
operator T*(); // conversion to pointer to any type
};
// out-of-class definition
template<typename T>
A::operator T*() { return nullptr; }
// explicit specialization for char*
template<>
A::operator char*() { return nullptr; }
// explicit instantiation
template A::operator void*();
int main()
{
A a;
int* ip = a.operator int*(); // explicit call to A::operator int*()
}Во время разрешения перегрузки специализации шаблонов функций преобразования не находятся с помощью поиска по имени. Вместо этого рассматриваются все видимые шаблоны функций преобразования, и каждая специализация, полученная с помощью вывода аргументов шаблона (у которого есть особые правила для шаблонов функций преобразования), используется так, как будто она найдена с помощью поиска по имени.
Объявления с использованием в производных классах не могут ссылаться на специализации шаблонов функций преобразования из базовых классов.
| Шаблон функции преобразования пользователя не может иметь выведенный тип возвращаемого значения: 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 запрещен |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/member_template