在C#中,您可以非常简单地定义自定义枚举,例如:
public IEnumerable<Foo> GetNestedFoos()
{
foreach (var child in _SomeCollection)
{
foreach (var foo in child.FooCollection)
{
yield return foo;
}
foreach (var bar in child.BarCollection)
{
foreach (var foo in bar.MoreFoos)
{
yield return foo;
}
}
}
foreach (var baz in _SomeOtherCollection)
{
foreach (var foo in baz.GetNestedFoos())
{
yield return foo;
}
}
}
(这可以使用LINQ和更好的封装来简化,但这不是问题的关键。)
在C ++ 11中,您可以执行类似的枚举,但AFAIK需要访问者模式:
template<typename Action>
void VisitAllFoos(const Action& action)
{
for (auto& child : m_SomeCollection)
{
for (auto& foo : child.FooCollection)
{
action(foo);
}
for (auto& bar : child.BarCollection)
{
for (auto& foo : bar.MoreFoos)
{
action(foo);
}
}
}
for (auto& baz : m_SomeOtherCollection)
{
baz.VisitAllFoos(action);
}
}
有没有办法做更像第一个的东西,函数返回一个可以在外部迭代而不是在内部调用访问者的范围?
(我并不是指构建一个std::vector<Foo>
并返回它 - 它应该是一个就地枚举。)
我知道Boost.Range库,我怀疑它会参与解决方案,但我对它并不是特别熟悉。
我也知道可以定义自定义迭代器来做这种事情(我也怀疑可能会参与答案)但是我正在寻找一些易于编写的东西,理想情况下并不比这里显示的示例和可组合(如_SomeOtherCollection
)。
我更喜欢不需要调用者使用lambdas或其他函子的东西(因为这只会使它成为访问者),虽然我不介意在需要时在内部使用lambdas(但仍然希望在那里避免使用它们)太)。
答案 0 :(得分:0)
如果我正确理解您的问题,您希望对集合的所有元素执行某些操作。
C ++有一组广泛的迭代器操作,在iterator header中定义。大多数集合结构(包括您引用的std::vector
)具有.begin
和.end
方法,这些方法不带参数并将迭代器返回到结构的开头和结尾。这些迭代器有一些可以手动执行的操作,但它们的主要用途是algorithm header的形式,它定义了几个非常有用的迭代函数。
在您的特定情况下,我相信您需要for_each
函数,它需要一个范围(作为开始结束迭代器)和一个要应用的函数。因此,如果您有一个名为action
的函数(或函数对象),并且您希望将其应用于名为data
的向量,则以下代码将是正确的(假设所有必需的标题都包含在内):< / p>
std::for_each(data.begin(), data.end(), action);
请注意,for_each
只是算法标头提供的众多功能之一。它还提供了搜索集合,复制一组数据,对列表进行排序,查找最小值/最大值以及更多内容的函数,这些函数都可以通过任何具有迭代器的结构进行处理。如果即使这些还不够,您也可以通过阅读迭代器支持的操作来编写自己的。只需定义一个模板函数,它接受不同类型的迭代器并记录您想要的迭代器类型。
template <typename BidirectionalIterator>
void function(BidirectionalIterator begin, BidirectionalIterator end) {
// Do something
}
最后要注意的是,如果您知道大小,到目前为止提到的所有操作也可以在阵列上正确运行。您不必撰写.begin
和.end
,而是撰写+ 0
和+ n
,其中n
是数组的大小。为了将数组的类型衰减为指针以使其成为有效的迭代器,通常需要简单的零添加,但是数组指针确实是随机访问迭代器,就像任何其他容器迭代器一样。
答案 1 :(得分:0)
您可以做的是编写自己的适配器函数,并使用相同类型的不同元素范围调用它。
这是一个未经测试的解决方案,可能需要进行一些调整才能使其编译,但它会给你一个想法。它使用可变参数模板从集合移动到下一个集合。
template<typename Iterator, Args...>
visitAllFoos(std::pair<Iterator, Iterator> collection, Args&&... args)
{
std::for_each(collection.first, collection.second, {}(){ // apply action });
return visitAllFoos(std::forward<Args>(args)...);
}
//you can call it with a sequence of begin/end iterators
visitAllFoos(std::make_pair(c1.begin(), c1,end()), std::make_pair(c2.begin(), c2,end()))
答案 2 :(得分:0)
我相信,您尝试做的事情可以通过Boost.Range完成,特别是使用join
和any_range(如果您想要隐藏类型,则需要后者容器并从界面中删除joined_range
。
但是,由此产生的解决方案在复杂性和性能方面都不太实用 - 主要是因为joined_range
引发的嵌套any_range
和类型擦除开销。就个人而言,我只是构建std::vector<Foo*>
或使用访问。
答案 3 :(得分:0)
您可以在boost::asio::coroutine
的帮助下完成此操作;请参阅https://pubby8.wordpress.com/2014/03/16/multi-step-iterators-using-coroutines/和http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/overview/core/coroutine.html上的示例。