Печать любого контейнера STL
Как бы вы писали библиотеку, которая при включении перегружает оператор <<
для любого * существующего контейнера STL?
- Кроме адаптеров, таких как стек и очередь, поскольку вы не можете их перебирать.
Одно из требований, предъявляемых к себе, состоит в том, что оно не должно содержать никаких файлов заголовков контейнеров. Это наполнило бы окончательный исполняемый файл излишне. Имеет смысл включить этот заголовок после контейнеров, с которыми я хотел бы работать. Это ограничение подразумевает использование templates
или macros
.
Я ищу советы и указатели, поэтому, пожалуйста, не просто публикуйте полностью рабочий код, который может это сделать! Реализация этого сама является частью процесса обучения. Небольшие фрагменты кода, которые демонстрируют, как некоторые вещи работают, приветствуются.
Что я сделал до этого момента:
Я перегрузил оператор <<
для каждого контейнера с другой подписью шаблона. Проблема, с которой я столкнулся с этим подходом, заключается в том, что существуют контейнеры с одинаковым количеством параметров шаблона, но некоторые содержат std::pair
-s, другие single values
. Точнее, это случай std::map
и std::multimap
vs std::unordered_set
и std::unordered_multiset
.
Это столкновение заставляет меня либо реализовать нетермическую версию одной из пар, либо создать способ различать std::pair
-s и single values
. Однако я действительно не знаю, как сделать вторую. Причина в том, что я не "как это сделать" напрямую, так это то, что я начинаю верить, что этого можно избежать полностью с лучшим общим дизайном.
Заранее благодарю!
Что сработало для меня:
- Перегрузка
operator<<
для std::pair
- Перегрузка
operator<<
для каждого контейнера STL с разными аргументами шаблона
Примечание:
-
template <typename T1>
и template <typename T1, typename T2>
отличаются -
template <typename T1, typename T2>
и template <typename T1, size_t T2>
являются отличается -
template <typename T1, typename T2>
и template <typename C1, typename C2>
НЕ разные
- Поместите их в пространство имен, чтобы убедиться, что ваши операторы не столкнутся с другими операциями, которые вам могут понадобиться в будущем.
Используйте "шаблоны в шаблонах", поэтому, например, функция будет выглядеть так:
template <typename Type, template <typename TYPE> class TClass>
void func(TClass<Type>& tc) {
if (tc.somethingTrue())
tc.doStuff();
}
Ответы
Ответ 1
Вы можете перегрузить operator<<
в качестве шаблона, который принимает аргумент шаблона шаблона (т.е. любой контейнер).
Затем вы могли бы предоставить две перегрузки функции шаблона (например, print
), чтобы одна перегрузка была специализирована на std::pair
.
template<typename T>
std::ostream& print(std::ostream &out, T const &val) {
return (out << val << " ");
}
template<typename T1, typename T2>
std::ostream& print(std::ostream &out, std::pair<T1, T2> const &val) {
return (out << "{" << val.first << " " << val.second << "} ");
}
template<template<typename, typename...> class TT, typename... Args>
std::ostream& operator<<(std::ostream &out, TT<Args...> const &cont) {
for(auto&& elem : cont) print(out, elem);
return out;
}
LIVE DEMO
Ответ 2
Очень простая попытка решить эту проблему:
template<typename T>
void print_container(std::ostream& os, const T& container, const std::string& delimiter)
{
std::copy(std::begin(container),
std::end(container),
std::ostream_iterator<typename T::value_type>(os, delimiter.c_str()));
}