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, " "));