Чтобы решить эту проблему, нам нужно T
:
- Уметь стирать и хранить экземпляр произвольного типа;
- быть конвертируемым из такого экземпляра;
- Перегрузите оператор
<<
и динамически перенаправьте его на экземпляр со стертым типом.
В зависимости от того, ограничен ли ваш список возможных типов или нет, мы можем отложить большую часть тяжелой работы до boost::variant
или boost::any
(соответственно std::variant
или std::any
в C ++ 17 и выше) .
Версия variant
проста:
template <class... Ts>
struct StreamableVariant : boost::variant<Ts...> {
using boost::variant<Ts...>::variant;
friend decltype(auto) operator << (std::ostream &os, StreamableVariant const &sv) {
return boost::apply_visitor([&](auto const &o) -> decltype(auto) {
return os << o;
}, sv);
}
};
// Usage
std::function<StreamableVariant<int, std::string>(int,int)> tf;
Версия any
немного сложнее, поскольку нам нужно вручную стереть функциональность потоковой передачи, пока мы еще знаем тип объекта во время создания:
struct StreamableAny : boost::any {
template <class T>
StreamableAny(T &&t)
: boost::any{std::forward<T>(t)}
, _printMe{[](std::ostream &os, StreamableAny const &self) -> decltype(auto) {
return os << boost::any_cast<T const &>(self);
}}{ }
private:
friend std::ostream &operator << (std::ostream &os, StreamableAny const &sa) {
return sa._printMe(os, sa);
}
std::ostream &(*_printMe)(std::ostream &os, StreamableAny const &);
};
// Usage
std::function<StreamableAny(int,int)> tf;