为什么没有范围 - 为std :: istream_iterator查找开始和结束的重载?

Vek*_*ksi 10 c++ istream-iterator visual-c++ c++11 visual-studio-2012

我有这样的代码

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();
Run Code Online (Sandbox Code Playgroud)

其中std::istream_iterator<std::string>begin()end() 定义如下

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>();
}
Run Code Online (Sandbox Code Playgroud)

这也是马克·尼尔森在Dobb博士这里所写的内容.唉,代码无法在我的Visual Studio 2012上使用错误消息进行编译

错误C3312:找不到类型'std :: istream_iterator <_Ty>'的可调用'begin'函数

错误C3312:找不到类型'std :: istream_iterator <_Ty>'的可调用'end'函数

问题:有没有我没有注意到的东西,编译器中的错误(不太可能,但只是以防万一)或......好吧,任何想法?


根据Xeo的建议,这些问题会得到很大的清理.为了提供更多的背景和参考,这与我在Stackoverflow上的另一个问题有关,我想知道如何使基于行的解析比通常的循环更清晰.从互联网上进行了一些编码和检查,我的工作草图如下

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();
Run Code Online (Sandbox Code Playgroud)

但是我试图解决这个问题.我认为编写代码无法编译而不喜欢它会更自然

for(auto& entry : istream_range<std::string>(file)
Run Code Online (Sandbox Code Playgroud)

请注意不同的迭代器.该delimeter_tokens的定义如下纳瓦兹好心显示在这里(代码不重复),并istream_range在码合成博客这里.我认为开始和结束实现应该有效,正如前面提到的Code Synthesis博客文章中所宣传的那样

最后一条规则(独立的begin()和end()函数的回退)允许我们以非侵入方式将现有容器调整到基于范围的for循环接口.

因此我的问题与所有(红色)相关背景.

Xeo*_*Xeo 7

如果对native(T foo[N])和member begin/ 的特殊处理end不产生任何结果,则Ranged-for依赖于ADL .

§6.5.4 [stmt.ranged] p1

  • 否则,开始-EXPR最终EXPRbegin(__range)end(__range)分别在那里beginend中查找与参数相关的查找(3.4.2) .出于此名称查找的目的,namespace std是关联的命名空间.

你的问题是,关联的命名空间std::istream_iterator(显然)namespace std,而不是全局命名空间.

§3.4.2 [basic.lookup.argdep] p2

对于T函数调用中的每个参数类型,都有一组零个或多个关联的命名空间以及一组零个或多个要考虑的关联类.命名空间和类的集合完全由函数参数[...]的类型决定.

  • 如果T是基本类型,则其关联的命名空间和类集都是空的.
  • 如果T是类类型(包括联合),则其关联的类是:类本身; 它所属的成员,如果有的话; 及其直接和间接基类.其关联的名称空间是其关联类是成员的名称空间.此外,如果T是类模板特化,其关联的名称空间和类还包括:与为模板类型参数[...]提供的模板参数类型相关联的名称空间和类.

请注意第二个项目符号的最后一个(引用)部分.它基本上意味着使用作为全局命名空间成员的类作为模板参数使代码工作:

#include <iterator>
#include <iostream>

template<class T>
std::istream_iterator<T> begin(std::istream_iterator<T> is){
  return is;
}
template<class T>
std::istream_iterator<T> end(std::istream_iterator<T>){
  return std::istream_iterator<T>();
}

struct foo{};

std::istream& operator>>(std::istream& is, foo){
  return is;
}

int main(){
  for(foo f : std::istream_iterator<foo>(std::cin))
  //                                ^^^
  // make global namespace one of the associated namespaces
    ;
}
Run Code Online (Sandbox Code Playgroud)