Namespaces
Variants

Member templates

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Объявления шаблонов ( 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 запрещен