我没有具体的LINQ经验,但
Boost.Iterator库似乎接近您所提到的内容。
其思想是有函数(我理解的是,在LINQ中,它们采用扩展方法的形式,但这并不是基本的),需要一个迭代器和一个函数,将它们组合起来创建一个新的迭代器。
LINQ的“Where”对应于
make_filter_iterator
:
std::vector<int> vec = ...
// An iterator skipping values less than "2":
boost::make_filter_iterator(_1 > 2, vec.begin())
LINQ中的“Select”映射到
make_transform_iterator
:
using namespace boost::lambda;
//An iterator over strings of length corresponding to the value
//of each element in "vec"
//For example, 2 yields "**", 3 "***" and so on.
boost::make_transform_iterator(construct<std::string>('*', _1), vec.begin())
它们可以被组合:
//An iterator over strings of length corresponding to the value of each element
// in "vec", excluding those less than 2
std::vector<int> vec = ...
boost::make_transform_iterator(construct<std::string>('*', _1),
boost::make_filter_iterator(_1 > 2, vec.begin())
)
然而,这里有一些令人烦恼的问题:
make_xxx_iterator(some_functor, some_other_iterator)
返回的类型是xxx_iterator<type_of_some_functor, type_of_some_iterator>
- 使用boost::bind、lambda或phoenix创建的函数对象类型很快变得难以管理和编写。
这就是为什么在上面的代码中我避免将
make_xxx_iterator
的结果赋值给一个变量。C++0x的"auto"特性在这里非常受欢迎。
但是,C++迭代器不能单独存在:它们必须成对出现才能有用。因此,即使使用"auto",它仍然很冗长:
auto begin = make_transform_iterator(construct<std::string>('*', _1),
make_filter_iterator(_1 > 2, vec.begin())
);
auto end = make_transform_iterator(construct<std::string>('*', _1),
make_filter_iterator(_1 > 2, vec.end())
);
避免使用lambda会让事情变得啰嗦,但仍然可控:
struct MakeStringOf{
MakeStringOf(char C) : m_C(C){}
char m_C;
std::string operator()(int i){return std::string(m_C, i);}
};
struct IsGreaterThan{
IsGreaterThan(int I) : m_I(I){}
int m_I;
bool operator()(int i){return i > m_I;}
};
typedef boost::filter_iterator<
IsGreaterThan,
std::vector<int>::iterator
> filtered;
typedef boost::transform_iterator<
MakeStringOf,
filtered
> filtered_and_transformed;
filtered_and_transformed begin(
MakeStringOf('*'),
filtered(IsGreaterThan(2), vec.begin())
);
filtered_and_transformed end(
MakeStringOf('*'),
filtered(IsGreaterThan(2), vec.end())
);
(尚未)Boost.RangeEx库在这方面很有潜力,因为它允许将两个迭代器组合成单个范围。类似这样:
auto filtered_and_transformed = make_transform_range(
make_filter_range(vec, _1 > 2),
construct<std::string>('*', _1)
);