for循环-等价于C++11中的枚举器



在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(。

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

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

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
}

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

您可以编写自己的适配器函数,并用相同类型的不同范围的元素调用它。

这是一个未经测试的解决方案,可能需要一些调整才能编译,但它会给你一个想法。它使用可变模板从一个集合移动到下一个集合。

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()))

我相信,您想要做的事情可以用Boost.Range来完成,尤其是用join和any_Range(如果您想隐藏容器的类型并从接口中删除joined_range,则需要后者(。

然而,所得到的解决方案在复杂性和性能方面都不太实用,这主要是因为嵌套的joined_rangeany_range引起的类型擦除开销。就我个人而言,我只会构建std::vector<Foo*>或使用访问。

您可以在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.

相关内容

  • 没有找到相关文章