Search code examples
c++c++20c++23

Is there a better way to generic print containers in C++20/C++23?


I use the following code to print generic C++ containers and exclude strings (thanks to this SO answer).

#include <iostream>
#include <vector>

template <typename T,
          template <typename ELEM, typename ALLOC = std::allocator<ELEM>>
          class Container>
std::ostream &operator<<(std::ostream &out, const Container<T> &container) {
  out << "[";
  bool first = true;
  for (const auto &elem : container) {
    if (first)
      first = false;
    else
      out << ", ";
    out << elem;
  }
  return out << "]";
}

int main() {
  std::vector<std::vector<int>> v{{1, 2}, {3, 4}};
  std::cout << v << "\n";
  return 0;
}

Which prints the 2d vector correctly

[[1, 2], [3, 4]]

And does not mess with outputting strings. Is there a cleaner (concept-based?) way to write the template in C++20 or C++23?


Solution

  • With concepts, you might require only container/range types and discard string-like types, something like:

    template <typename Container>
    requires std::ranges::input_range<const Container>
         && (!std::convertible_to<const Container, std::string_view>)
    std::ostream &operator<<(std::ostream &out, const Container& container)
    {
      out << "[";
      const char* sep = "";
      for (const auto &elem : container) {
        out << sep << elem;
        sep = ", ";
      }
      return out << "]";
    }
    

    Demo

    With the caveats that generic template should involve at least one user type. Else it might conflict with possible future operator<< for std (or other library) containers...