Пространства имён
Варианты
Действия

Шаблоны элементы

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
 

Объявления шаблонов (классов, функций и переменных (начиная с 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 запрещён