Skip to the content of the web site.

Printing and string conversions

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();
}