我没有 LINQ 的具体经验,但 Boost.Iterator 库似乎接近您所指的内容。
这个想法是拥有函数(IIUC,在 LINQ 中,它们采用扩展方法的形式,但这不是基本的),采用迭代器和函数,将它们结合起来创建一个新的迭代器。
LINQ“位置”映射到make_filter_iterator:
std::vector<int> vec = ...;
// An iterator skipping values less than "2":
boost::make_filter_iterator(_1 > 2, vec.begin())
LINQ“选择”映射到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“自动”功能将在那里非常受欢迎。
但是,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)
);