I wanted to write a generic <<
for any range
and I ended up with this:
std::ostream& operator << (std::ostream& out, std::ranges::range auto&& range) {
using namespace std::ranges;
if (empty(range)) {
return out << "[]";
}
auto current = begin(range);
out << '[' << *current;
while(++current != end(range)) {
out << ',' << *current;
}
return out << ']';
}
Tested like so:
int main() {
std::vector<int> ints = {1, 2, 3, 4};
std::cout << ints << '\n';
}
it works perfectly and outputs:
[1,2,3,4]
But, when tested with:
int main() {
std::vector<int> empty = {};
std::cout << empty << '\n';
}
it outputs, unexpectedly:
[[,], ]
Running this code with a debugger, I came to a conclusion that the problem with empty range is that we run the return out << "[]";
. Some C++ magic decided that my, just written,
std::ostream& operator << (std::ostream& out, std::ranges::range auto&& range);
is a better match then the, provided in <ostream>
,
template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
const char* s );
so instead of just sending "[]"
to the output stream like we are used to see, it recurses back to itself, but with "[]"
as the range
argument.
What is the reason for that being a better match? Can I fix this in a more elegant manner compared to sending [
and ]
separately?
EDIT: It appears that this is most likely a bug in GCC 10.1.0, since the newer versions reject the code.