To iterate over an input stream, we would usually use a std::istream_iterator
like so:
typedef std::istream_iterator input_iterato
I attempted to pursue the idea of specializing std::begin
and std::end
for classes derived from std::basic_istream
(I'm not so great at this template metaprogramming business):
namespace std
{
template
typename
std::enable_if<
std::is_base_of, C>::value,
std::istream_iterator>::type
begin(C& c)
{
return {c};
}
template
typename
std::enable_if<
std::is_base_of, C>::value,
std::istream_iterator>::type
end(C& c)
{
return {};
}
}
Actually, it works pretty well. I didn't create versions that take const C&
because I don't think it makes sense to extract from a const stream (and I had errors when I tried to do so). I'm also not sure if I can make this more move friendly. So now I can print out the contents of myfile
like so::
std::ifstream file("myfile");
std::copy(begin(file), end(file), std::ostream_iterator(std::cout, " "));
So these begin
and end
functions work as expected. However, it falls flat when used in a range-based for
loop. std::basic_istream
classes are derived from std::ios_base
which already has a member called end
(it's a flag for seeking within a stream). Once the range-based for
loop finds this, it just gives up because it can't find a corresponding begin
(not to mention that end
is not the right kind of entity):
main.cpp:35:33: error: range-based ‘for’ expression of type ‘std::basic_ifstream’ has an ‘end’ member but not a ‘begin’
The only alternative that works in both situations, as others have mentioned, is to create a wrapper object. Unfortunately that end
member in std::ios_base
completely ruins any chance of implementing this in a nice way.