You get this error because your function matches every call to << with a std::ostream& on the left.
template <typename C>
std::ostream& operator<<(std::ostream& os, const C& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
return os;
}
When you write os << "[", the compiler finds multiple operator<< functions to call; your's is one of them. By adding a global operator<< that's templated to take any type, you intercept basically every call to operator<<.
The cleanest way you could do this is to define a new function, say print_collection:
template <typename C>
void print_collection(std::ostream& os, const C& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
}
If you really want to define an operator<<, this gets more tricky. You could do this:
template <typename C>
std::ostream& operator<<(std::ostream& os, const std::vector<C>& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
return os;
}
However, if the standard library decides to add an operator<< of their own for std::vector, your code will break.
I'd strongly recommend that if you wanted to add such an operator<<, you do it for your own type. Something like this:
template <typename Iter>
class Range {
Iter begin_;
Iter end_;
public:
Range() = default;
Range(Iter begin, Iter end)
: begin_{ begin }
, end_{ end }
{}
auto begin() const { return begin_; }
auto end() const { return end_; }
};
template <typename Iter>
auto range(Iter begin, Iter end) {
return Range<Iter>{ begin, end };
}
template <typename C>
auto range(const C& collection) {
return range(std::begin(collection), std::end(collection));
}
template <typename Iter>
std::ostream& operator<<(std::ostream& os, const Range<Iter>& range) {
os << "[";
for (const auto& v : range) {
os << v << " ";
}
os << "]";
return os;
}
Then you could use it like this:
std::vector<int> vec = ...;
std::cout << range(vec);