I use the following code to print generic C++ containers and exclude strings (thanks to [this](https://stackoverflow.com/a/1154683/2599610) 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?