相当于C ++ 11中的枚举数?

时间:2015-07-03 02:37:37

标签: c++11 for-loop boost boost-range

在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(但仍然希望在那里避免使用它们)太)。

4 个答案:

答案 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完成,特别是使用joinany_range(如果您想要隐藏类型,则需要后者容器并从界面中删除joined_range

但是,由此产生的解决方案在复杂性和性能方面都不太实用 - 主要是因为joined_range引发的嵌套any_range和类型擦除开销。就个人而言,我只是构建std::vector<Foo*>或使用访问。

答案 3 :(得分:0)