2015-07-03 56 views
1

在C#中,您可以定义自定义枚举很平凡,如:C++ 11中的枚举器等价吗?

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,你可以做类似的枚举,但据我所知它需要一个访问者模式来代替:

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(如果需要的话)(但仍然倾向于避免它们),我宁愿不要求调用者使用lambda表达式或其他函数那里)。

回答

0

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

C++有一套广泛的迭代器操作,在iterator header中定义。大多数收集结构(包括您参考的std::vector)都有.begin.end方法,它们不采用任何参数并将迭代器返回到结构的开始和结束处。这些迭代器有一些可以手动执行的操作,但它们的主要用途是algorithm header,它定义了几个非常有用的迭代函数。

在你的具体情况下,我相信你需要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是数组的大小。为了将数组的类型衰减为一个指针,使其成为一个有效的迭代器,通常需要简单的零加法,但数组指针确实是随机访问迭代器,就像任何其他容器迭代器一样。

+0

我说我对访客模式不感兴趣。 – Miral

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

我说我对访客模式不感兴趣。 – Miral

+0

这不是一个访问者模式本身https://en.wikipedia.org/wiki/Visitor_pattern –

0

我相信,你正在试图做的可以用Boost.Range来完成,特别是与joinany_range什么(后者是,如果你想隐藏的各类容器,并删除joined_range需要从界面)。

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

+0

使用访问来构建一个'std :: vector '是我现在是如何做的,但作为我说我并不特别满意。但谢谢你的确认。 – Miral

0
+0

对不起,延迟回复。这是一个可爱的想法,但使用无堆栈协程来处理这种情况将需要展开基于范围的对于迭代器(因为本地值不会被保留 - 任何需要在yield之间记住的东西必须是成员变量而不是局部变量),并且这需要相当大的代码(尽管没有协程一样严重)。使用堆栈协程可以避免这个问题,但对于这种情况来说,这种感觉过于沉重。 – Miral