C++ 输入流上基于范围的循环

C++ 输入流上基于范围的循环,c++,c++11,iterator,inputstream,C++,C++11,Iterator,Inputstream,要在输入流上迭代,我们通常会使用std::istream\u迭代器,如下所示: typedef std::istream_iterator<std::string> input_iterator; std::ifstream file("myfile"); for (input_iterator i(file); i != input_iterator(); i++) { // Here, *i denotes each element extracted

要在输入流上迭代,我们通常会使用
std::istream\u迭代器
,如下所示:

typedef std::istream_iterator<std::string> input_iterator;

std::ifstream file("myfile");
for (input_iterator i(file); i != input_iterator(); i++) {
  // Here, *i denotes each element extracted from the file
}
或:


是否通过依赖参数的查找找到它们并不重要,因为您可以将类和函数的专门化放在
std
命名空间中。

这里有一个可能的解决方案。不幸的是,它确实需要一个额外的结构:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <string>

struct S {
  std::istream& is;
  typedef std::istream_iterator<std::string> It;
  S(std::istream& is) : is(is) {}
  It begin() { return It(is); }
  It end() { return It(); }
};

int main () {
  std::ifstream file("myfile");
  for(auto& string : S(file)) {
    std::cout << string << "\n";
  }
}

一个明显的方法是为流使用一个简单的装饰器,提供类型和必要的接口。下面是它的样子:

template <typename T>
struct irange
{
    irange(std::istream& in): d_in(in) {}
    std::istream& d_in;
};
template <typename T>
std::istream_iterator<T> begin(irange<T> r) {
    return std::istream_iterator<T>(r.d_in);
}
template <typename T>
std::istream_iterator<T> end(irange<T>) {
    return std::istream_iterator<T>();
}

for (auto const& x: irange<std::string>(std::ifstream("file") >> std::skipws)) {
    ...
}
模板
结构irange
{
irange(std::istream&in):d_in(in){
标准::istream&d_in;
};
模板
std::istream_迭代器开始(irange r){
返回std::istream_迭代器(r.d_in);
}
模板
std::istream_迭代器端(irange){
返回std::istream_迭代器();
}
对于(auto-const&x:irange(std::ifstream(“文件”)>>std::skipws)){
...
}

我试图对从
std::basic\u istream
派生的类专门化
std::begin
std::end
(我不太擅长这个模板元编程业务):

因此,这些
开始
结束
函数按预期工作。但是,在基于范围的
for
循环中使用时,它会下降
std::basic_istream
类派生自
std::ios_base
,它已经有一个名为
end
的成员(它是在流中查找的标志)。一旦基于范围的
for
循环找到此项,它就会放弃,因为它找不到相应的
begin
(更不用说
end
不是正确的实体类型):

main.cpp:35:33:错误:基于范围的'for'表达式类型'std::basic_ifstream'有一个'end'成员,但没有'begin'


正如其他人所提到的,在这两种情况下唯一可行的方法是创建一个包装器对象。不幸的是,
std::ios_base
中的
end
成员完全破坏了以良好方式实现此功能的任何机会。

但是,据我所知,您不允许添加重载,并且由于您不能部分专门化函数,你要么需要为所有
std::istream_iterator
添加全部专业知识,要么只为你需要的专业知识添加专业知识。@PeterAlexander嗯,我不确定我是否理解。你这是什么意思“所有
std::istream\u迭代器的完全专业化”
?@PeterAlexander当然我们需要
std::basic\u istream
的专业化?忽略我,出于某种原因,我认为
开始
/
结束
需要一个
std::istream\u迭代器
,这只是我的问题,还是规范中很不清楚?似乎只有在
\u range
不是数组或类类型的情况下才能找到
std::begin()
std::end()
。是的,这不是最好的措辞,但我认为您应该将其理解为“如果它是一个类,并且它有.begin和.end,那么它将使用这些…否则”,即您可以提供免费函数。“
开始
结束
在类的范围内查找_range。。。如果其中一个。。。查找至少一个声明,
begin expr
end expr
\u范围。begin()
\u范围。end()
“-由于
std::ios\u base::end
确实存在(因此将找到
std::ifstream::end
),游戏已启动。
.begin()
将找不到,并且
.end()
将出现语法错误。FWIW Boost.Range提供。
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <string>

struct S {
  std::istream& is;
  typedef std::istream_iterator<std::string> It;
  S(std::istream& is) : is(is) {}
  It begin() { return It(is); }
  It end() { return It(); }
};

int main () {
  std::ifstream file("myfile");
  for(auto& string : S(file)) {
    std::cout << string << "\n";
  }
}
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <string>


struct ifstream : std::ifstream {
  // using std::ifstream::ifstream; I wish g++4.7 supported inheriting constructors!
  ifstream(const char* fn) : std::ifstream(fn) {}
  typedef std::istream_iterator<std::string> It;
  It begin() { return It(*this); }
  It end() { return It(); }
};

int main () {
  ifstream file("myfile");
  for(auto& string : file) {
    std::cout << string << "\n";
  }
}
template <typename T>
struct irange
{
    irange(std::istream& in): d_in(in) {}
    std::istream& d_in;
};
template <typename T>
std::istream_iterator<T> begin(irange<T> r) {
    return std::istream_iterator<T>(r.d_in);
}
template <typename T>
std::istream_iterator<T> end(irange<T>) {
    return std::istream_iterator<T>();
}

for (auto const& x: irange<std::string>(std::ifstream("file") >> std::skipws)) {
    ...
}
namespace std
{
  template <typename C>
  typename
  std::enable_if<
    std::is_base_of<std::basic_istream<typename C::char_type>, C>::value,
    std::istream_iterator<std::string>>::type
  begin(C& c)
  {
    return {c};
  }

  template <typename C>
  typename
  std::enable_if<
    std::is_base_of<std::basic_istream<typename C::char_type>, C>::value,
    std::istream_iterator<std::string>>::type
  end(C& c)
  {
    return {};
  }
}
std::ifstream file("myfile");
std::copy(begin(file), end(file), std::ostream_iterator<std::string>(std::cout, " "));