1

在 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);
    }
}

有没有办法做一些更像第一种的方法,函数返回一个可以在外部迭代而不是在内部调用访问者的范围?

(我的意思不是构造 astd::vector<Foo>并返回它——它应该是一个就地枚举。)

我知道 Boost.Range 库,我怀疑它会参与解决方案,但我对它并不是特别熟悉。

我也知道可以定义自定义迭代器来做这种事情(我也怀疑可能会涉及到答案),但我正在寻找易于编写的东西,理想情况下不会比显示的示例更复杂在这里,并且是可组合的(比如 with _SomeOtherCollection)。

我更喜欢不需要调用者使用 lambdas 或其他仿函数的东西(因为这只会让它再次成为访问者),尽管我不介意在需要时在内部使用 lambdas(但仍然希望在那里避免它们)。

4

4 回答 4

0

如果我正确理解您的问题,您希望对集合的所有元素执行一些操作。

C++ 有一组广泛的迭代器操作,定义在迭代器头文件中。大多数集合结构,包括std::vector您引用的集合结构,都具有不带参数并将迭代器返回到结构开头和结尾的方法.begin.end这些迭代器有一些可以手动执行的操作,但它们的主要用途是算法头的形式,它定义了几个非常有用的迭代函数。

在您的具体情况下,我相信您需要该for_each函数,该函数需要一个范围(作为一个开始到结束的迭代器)和一个要应用的函数。因此,如果您调用了一个函数(或函数对象)action,并且您想将其应用于一个名为 的向量data,则以下代码将是正确的(假设所有必要的标头都已正确包含):

std::for_each(data.begin(), data.end(), action);

请注意,这for_each只是算法头提供的众多功能之一。它还提供了搜索集合、复制一组数据、排序列表、查找最小值/最大值等等的功能,所有这些都可以泛化到任何具有迭代器的结构上。如果这些还不够,您可以通过阅读迭代器支持的操作来编写自己的。只需定义一个模板函数,该函数接受不同类型的迭代器并记录您想要的迭代器类型。

template <typename BidirectionalIterator>
void function(BidirectionalIterator begin, BidirectionalIterator end) {
    // Do something
}

最后一点是,到目前为止提到的所有操作也可以在数组上正确操作,前提是您知道数组的大小。不是写.beginand .end,而是写+ 0and ,数组的大小在+ n哪里。n为了将数组的类型衰减为指针以使其成为有效的迭代器,通常需要进行简单的零加法,但数组指针确实是随机访问迭代器,就像任何其他容器迭代器一样。

于 2015-07-03T03:36:49.387 回答
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()))
于 2015-07-03T09:47:32.937 回答
0

我相信,你想要做的事情可以用 Boost.Range 来完成,尤其是joinany_range(如果你想隐藏容器的类型并joined_range从界面中删除,则需要后者)。

joined_range然而,由此产生的解决方案在复杂性和性能方面都不是很实用——主要是因为any_range. 就个人而言,我只会构建std::vector<Foo*>或使用访问。

于 2015-07-03T21:36:00.580 回答
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

于 2015-07-05T23:50:48.877 回答