I have this example which is about STL containers so i'm reading about them, It gets so tedious for me to repeatedly use a range-for
loop to print the content of a container. So I thought about overloading the insertion operator <<
thus I can write: std::cout << container << std::endl;
.
template<class T>
std::ostream& operator<<(std::ostream& out, const std::list<T>& v) {
for (const auto& e : v)
out << e << ", ";
return out;
}
int main() {
std::list<int> vi{ 10, 24, 81, 57, 2019 };
vi.pop_back();
std::cout << vi << std::endl; // 10, 24, 81, 57,
std::deque<std::string> names{ "Hello", "STL Containers" };
std::cout << names << std::endl; // error here. Bacause I've not overloaded << to take a std::deque<T>
}
As you can see above I find it so comfortable to print a list
of some type. The problem is I can only print a list but not other sort of container like a vector
or deque
...
So how can I overload <<
to take a container of type T<U>
or Should I specialize it for all the containers?
In a real example Shouldn't I do that?
You could provide the following overload :
template <class...>
using void_t = void;
template <class... Args>
constexpr bool exists_t_v(){
return std::is_same<void_t<Args...>, void>::value;
}
template <class Container, class = std::enable_if<
exists_t_v<
decltype(std::begin(std::declval<Container>())),
decltype(std::end(std::declval<Container>()))
>()
>>
std::ostream& operator<<(std::ostream& os, Container&& container){
for(const auto& e : std::forward<Container>(container))
os << e << ',';
return os;
}
This will let any instance container
of the type Container
for which std::begin(container)
and std::begin(container)
are defined to use the overload.
One of the issues with solutions like these is std::string
support as well as any type that already provide its own overload for which std::begin()
and std::end()
are defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With