There are two approaches to harmonizing the printing of containers with std::cout and related output-stream objects. You can either overload operator<< to print a specific container, or you can arrange it to print a std::pair of iterators. This is demonstrated here:
#include <iostream>
#include <vector>
#include <utility>
// Function declarations
int main();
template <typename T>
std::ostream &operator<<( std::ostream &out, std::vector<T> const &obj );
template <typename ITR>
std::ostream &operator<<( std::ostream &out, std::pair<ITR, ITR> const &obj );
// Function definitions
int main() {
std::vector<int> data{ 1, 2, 5, 4, 3, 5, 6, 7, 6, 8, 9, 6, 10, 13, 12, 14 };
std::cout << data << std::endl;
std::cout << std::make_pair( data.begin(), data.end() ) << std::endl;
return 0;
}
template <typename T>
std::ostream &operator<<( std::ostream &out, std::vector<T> const &obj ) {
out << "{";
if ( !obj.empty() ) {
auto itr{ obj.begin() };
out << *itr++;
while ( itr != obj.end() ) {
out << ", " << *itr++;
}
}
return out << "}";
}
template <typename ITR>
std::ostream &operator<<( std::ostream &out, std::pair<ITR, ITR> const &obj ) {
out << "{";
if ( obj.first != obj.second ) {
auto itr{ obj.first };
out << *itr++;
while ( itr != obj.second ) {
out << ", " << *itr++;
}
}
return out << "}";
}
This is a nice solution by Jon Hanson that abstracts out the container:
// Function declaration
template <
typename S,
template <typename T, typename ALLOC = std::allocator<T>> class C
>
std::ostream &operator<<( std::ostream &out, C<S> const &container );
// Function definition
template <
typename S,
template <typename T, typename ALLOC = std::allocator<T>> class C
>
std::ostream &operator<<( std::ostream &out, C<S> const &container ) {
auto itr{ container.begin() };
out << "{";
if ( itr != container.end() ) {
out << *itr++;
while ( itr != container.end() ) {
out << ", " << *itr++;
}
}
return out << "}";
}
The following is an equivalent algorithm that converts a container into a std::string:
#include <sstream>
// Function declaration
template <
typename S,
template <typename T, typename ALLOC = std::allocator<T>> class C
>
std::string to_string( C<S> const &container, std::string separator = ", " );
// Function definition
template <
typename S,
template <typename T, typename ALLOC = std::allocator<T>> class C
>
std::string to_string( C<S> const &container, std::string separator ) {
std::stringstream sstream{};
sstream << "{";
auto itr{ container.begin() };
if ( itr != container.end() ) {
sstream << *itr++;
while ( itr != container.end() ) {
sstream << separator << *itr++;
}
}
sstream << "}";
return sstream.str();
}