На самом деле, сценарий использования параметров шаблона шаблона довольно очевиден. Как только вы узнаете, что в C ++ stdlib есть дыра, не позволяющая определять операторы потокового вывода для стандартных типов контейнеров, вы начинаете писать что-то вроде:
template<typename T>
static inline std::ostream& operator<<(std::ostream& out, std::list<T> const& v)
{
out << '[';
if (!v.empty()) {
for (typename std::list<T>::const_iterator i = v.begin(); ;) {
out << *i;
if (++i == v.end())
break;
out << ", ";
}
}
out << ']';
return out;
}
Тогда вы поймете, что код для вектора такой же, ведь forward_list одинаков, на самом деле, даже для множества типов карт он все тот же. Эти классы шаблонов не имеют ничего общего, кроме мета-интерфейса / протокола, и использование параметра шаблона шаблона позволяет зафиксировать общность во всех из них. Прежде чем приступить к написанию шаблона, стоит проверить ссылку, чтобы напомнить, что контейнеры последовательности принимают 2 аргумента шаблона - для типа значения и распределителя. Пока по умолчанию используется allocator, мы все равно должны учитывать его существование в нашем шаблонном операторе <<: </p>
template<template <typename, typename> class Container, class V, class A>
std::ostream& operator<<(std::ostream& out, Container<V, A> const& v)
...
Вуаля, которая будет работать автоматически для всех существующих и будущих контейнеров последовательностей, придерживающихся стандартного протокола. Чтобы добавить карты в микс, нужно взглянуть на ссылку, чтобы заметить, что они принимают 4 параметра шаблона, поэтому нам понадобится другая версия оператора << выше с 4-аргументным шаблоном param. Мы также увидим, что std: pair пытается отображаться с помощью оператора 2-arg << для типов последовательностей, которые мы определили ранее, поэтому мы предоставим специализацию только для std :: pair. </p>
Кстати, с C + 11, который допускает шаблоны с переменным числом (и, следовательно, должен разрешать аргументы шаблона с переменным числом элементов), можно было бы иметь один оператор <<, чтобы управлять ими всеми. Например: </p>
#include <iostream>
#include <vector>
#include <deque>
#include <list>
template<typename T, template<class,class...> class C, class... Args>
std::ostream& operator <<(std::ostream& os, const C<T,Args...>& objs)
{
os << __PRETTY_FUNCTION__ << '\n';
for (auto const& obj : objs)
os << obj << ' ';
return os;
}
int main()
{
std::vector<float> vf { 1.1, 2.2, 3.3, 4.4 };
std::cout << vf << '\n';
std::list<char> lc { 'a', 'b', 'c', 'd' };
std::cout << lc << '\n';
std::deque<int> di { 1, 2, 3, 4 };
std::cout << di << '\n';
return 0;
}
выход
std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = float, C = vector, Args = <std::__1::allocator<float>>]
1.1 2.2 3.3 4.4
std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = char, C = list, Args = <std::__1::allocator<char>>]
a b c d
std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = int, C = deque, Args = <std::__1::allocator<int>>]
1 2 3 4