I have code like this
std::ifstream file(filename, std::ios_base::in);
if(file.good())
{
file.imbue(std::locale(std::locale(), new delimeter_tokens()));
for(auto& entry : std::istream_iterator<std::string>(file))
{
std::cout << entry << std::endl;
}
}
file.close();
where std::istream_iterator<std::string>
's begin()
and end()
are defined as follows
template<class T>
std::istream_iterator<T> begin(std::istream_iterator<T>& stream)
{
return stream;
}
template<class T>
std::istream_iterator<T> end(std::istream_iterator<T>& stream)
{
return std::istream_iterator<T>();
}
which is what Mark Nelson has also written about in Dr. Dobb's here. Alas, the code fails to compile on my Visual Studio 2012 with error messages
error C3312: no callable 'begin' function found for type 'std::istream_iterator<_Ty>'
and
error C3312: no callable 'end' function found for type 'std::istream_iterator<_Ty>'
Question: Is there something I haven't noticed, bug in the compiler (unlikely, but just in case) or... Well, any ideas?
This questions is cleaned up considerably, as advised by Xeo. To provide more background and references this is related to my other question on Stackoverflow, I was wondering how to make line based parsing cleaner than the usual loops. A bit of coding and checking from the internet, and I had a working sketch as follows
std::ifstream file(filename, std::ios_base::in);
if(file.good())
{
file.imbue(std::locale(std::locale(), new delimeter_tokens()));
for(auto& entry : istream_range<std::string>(file)
{
std::cout << entry << std::endl;
}
}
file.close();
but there was slight snag I tried to remedy. I think it would look more natural to write as in the code that fails to compile and not like
for(auto& entry : istream_range<std::string>(file)
Please, take a note of the different iterator. The delimeter_tokens is defined like Nawaz kindly has shown here (code not duplicated) and istream_range as in Code Synthesis blog here. I think the begin and end implementations should work, as advertised in the aforementioned Code Synthesis blog post
The last rule (the fallback to the free-standing begin()and end() functions) allows us to non-invasively adapt an existing container to the range-based for loop interface.
Thus my question with all the (ir)relevant background.
Because of argument depended lookup the compiler tries to find
begin()
andend()
in thestd
namespace. If you put your functions there, the code compiles.Since name lookup is a complicated issue in C++ I'm not entirely sure if the compiler is behaving correctly or not.
Ranged-for relies on ADL if the special handling for native array (
T foo[N]
) and memberbegin
/end
doesn't yield any results.§6.5.4 [stmt.ranged] p1
Your problem is, that the associated namespace of
std::istream_iterator
is (obviously)namespace std
, not the global namespace.§3.4.2 [basic.lookup.argdep] p2
Note the last (quoted) part of the second bullet. It basically means that using a class which is a member of the global namespace as the template argument makes the code work: