Я должен признать, что template
все еще кажутся мне чёрной магией.Я успешно использовал специализации шаблона в различных ситуациях (если у меня не было лучшей идеи), но никогда не использовал аргументы шаблона.Итак, я попытался ...
Когда я попробовал Coliru (компилятор g++ (GCC) 8.1.0
), я начал с -std=c++11
и вскоре получил несколько ужасных ошибок.К счастью, был также совет перейти на -std=c++17
.Я сделал, и вещи становятся намного лучше сразу.Я грубо вспомнил, что аргументы шаблона не очень хорошо поддерживаются в C ++ 11.Короткий взгляд на cppreference
подтвердил мою правоту:
Параметр шаблона шаблона
template <параметр-список> typename (C++ 17) |имя класса (необязательно) (1)шаблон <список параметров> имя типа (C ++ 17) |имя класса (необязательно) = по умолчанию (2)шаблон <список параметров> имя типа (C ++ 17) |класс ... имя (необязательно) (3) (начиная с C ++ 11)
1) Параметр шаблона шаблона с необязательным именем.2) Параметр шаблона шаблона с необязательным именем и значением по умолчанию.3) Пакет параметров шаблона шаблона с необязательным именем.
Получив это пояснение, я взял часть примера кода OP и добавил частичную специализацию для template
, где I
- оставил первый параметр
- специализированный второй параметр с
std::stack
.
На самом деле, это не сильно отличается от того, как специализируются шаблоны с аргументами типа или значения.
cppreference
имеет статью дляэто частичная специализация шаблона , но при использовании этого термина в качестве ключевого слова для поиска должно быть много обращений к книгам и учебным пособиям.(Я помню книгу, в которой я когда-то учил шаблоны C ++, тоже упомянул об этом -).
Итак, вот мой небольшой пример, чтобы продемонстрировать это:
#include <iostream>
#include <stack>
#include <vector>
template<typename T, template <typename...> typename U>
class ContainerT {
private:
U<T> _container;
public:
ContainerT(): _container() { }
~ContainerT() = default;
ContainerT(const ContainerT&) = default;
ContainerT& operator=(const ContainerT&) = default;
bool empty() const { return _container.empty(); }
void push(const T &data) { _container.push_back(data); }
T pop()
{
const T data = _container.back();
_container.pop_back();
return data;
}
};
template<typename T>
class ContainerT<T, std::stack> {
private:
std::stack<T> _container;
public:
ContainerT(): _container() { }
~ContainerT() = default;
ContainerT(const ContainerT&) = default;
ContainerT& operator=(const ContainerT&) = default;
bool empty() const { return _container.empty(); }
void push(const T &data) { _container.push(data); }
T pop()
{
const T data = _container.top();
_container.pop();
return data;
}
};
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
// for std::vector
DEBUG(ContainerT<int, std::vector> vec);
DEBUG(vec.push(1); vec.push(2); vec.push(3));
DEBUG(while (!vec.empty()) std::cout << vec.pop() << '\n');
// for std::stack
DEBUG(ContainerT<int, std::stack> stk);
DEBUG(stk.push(1); stk.push(2); stk.push(3));
DEBUG(while (!stk.empty()) std::cout << stk.pop() << '\n');
// done
return 0;
}
Очевидно, что ContainerT
устанавливается для ContainerT<int, std::vector>
.В противном случае метод push()
не может быть скомпилирован.
Для ContainerT<int, std::stack>
вместо этого используется специализация.(Опять же, в противном случае метод push()
не может быть скомпилирован.)
Вывод:
ContainerT<int, std::vector> vec;
vec.push(1); vec.push(2); vec.push(3);
while (!vec.empty()) std::cout << vec.pop() << '\n';
3
2
1
ContainerT<int, std::stack> stk;
stk.push(1); stk.push(2); stk.push(3);
while (!stk.empty()) std::cout << stk.pop() << '\n';
3
2
1
Live Demoна колиру