Объявления шаблонов (
class
,
function
, и
variables
(since C++14)
) могут находиться внутри
member specification
любого класса, структуры или объединения, которые не являются
local classes
.
Частичные специализации шаблонов-членов могут появляться как в области видимости класса, так и в области видимости охватывающего пространства имен. Явные специализации могут появляться в любой области видимости, где может появляться основной шаблон.
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 запрещен
|