在C#中是否存在IEnumerable <T>的标准C++等价物?

der*_*khh 37 c# c++ ienumerable

或者,如果T的枚举器只是列出所有元素,那么使用向量是否安全?

Ben*_*igt 48

在C++中不需要它,这就是为什么:

C#仅支持动态多态.因此,要创建可重用的算法,您需要一个所有迭代器都将实现的接口.那是IEnumerator<T>,并且IEnumerable<T>是一个返回迭代器的工厂.

另一方面,C++模板支持duck typing.这意味着您不需要通过接口约束泛型类型参数来访问成员 - 编译器将按名称查找模板的每个单独实例化的成员.

C++的容器和迭代器具有隐式接口这相当于.NET IEnumerable<T>,IEnumerator<T>,ICollection<T>,IList<T>,即:

对于容器:

  • iteratorconst_iteratortypedefs
  • begin() 成员函数 - 满足需要 IEnumerable<T>::GetEnumerator()
  • end()成员函数 - 而不是IEnumerator<T>::MoveNext()返回值

对于前向迭代器:

  • value_type 的typedef
  • operator++ - 代替 IEnumerator<T>::MoveNext()
  • operator*operator->-而不是IEnumerator<T>::Current
  • 引用返回类型operator*- 而不是IList<T>索引器设置器
  • operator==并且operator!=- 在.NET中没有真正的等价物,但是容器的end()匹配IEnumerator<T>::MoveNext()返回值

对于随机访问迭代器:

  • operator+,operator-,operator[]-而不是IList<T>

如果您定义了这些,那么标准算法将适用于您的容器和迭代器.不需要任何接口,不需要虚拟功能.不使用虚函数使得C++通用代码比等效的.NET代码更快,有时候要快得多.


注意:编写通用算法时,最好使用std::begin(container)std::end(container)不是容器成员函数.除了STL容器之外,这允许您的算法与原始数组(没有成员函数)一起使用.原始数组和原始指针满足容器和迭代器的所有其他要求,只有这个例外.

  • 这是消耗IEnumerable等价物的一个很好的解释,但是生成它们呢?如果我想定义一个暴露成员的接口,我可以做begin()和end()但不关心实现该成员的特定类型,该怎么办? (3认同)
  • Andrei Alexandrescu 不同意你的观点。请参阅“迭代器必须去”:http://zao.se/~zao/boostcon/09/2009_presentations/wed/iterators-must-go.pdf C++/D Ranges 是建议的替代品,您不知道,范围几乎完全匹配 .NET IEnumerator 接口。 (3认同)
  • @naasking:我不知道这如何构成“分歧”。现在我们有两种方法可以在没有虚拟调度接口的情况下迭代范围。您声称范围是“IEnumerator”表明您对“IEnumerator”实际上是什么一无所知。范围是鸭子类型,.NET `IEnumerable` 是动态调度的。并且请注意,尽管范围具有所有优点,但仍然不是标准 C++ 中处理事情的规范方式。 (2认同)

fre*_*low 8

标准的C++方法是传递两个迭代器:

template<typename ForwardIterator>
void some_function(ForwardIterator begin, ForwardIterator end)
{
    for (; begin != end; ++begin)
    {
        do_something_with(*begin);
    }
}
Run Code Online (Sandbox Code Playgroud)

示例客户端代码:

std::vector<int> vec = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(vec.begin(), vec.end());

std::list<int> lst = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(lst.begin(), lst.end());

int arr[] = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(arr + 0, arr + 8);
Run Code Online (Sandbox Code Playgroud)

Yay泛型编程!


J.P*_*.P. 8

据我所知,如果我们严格地坚持这个问题,答案是否定的。人们一直在回答 C++ 中可用的替代品是什么,这可能是很好的信息,但不是答案,而且 OP 很可能已经知道了。

我完全不同意“不需要”,只是 C++ 和 .NET 标准库的设计不同。IEnumerable<> 的主要特点是它是多态的,因此它使调用者能够使用他想要的任何类(数组、列表、集合等),同时仍然提供编译时强类型,即使在库 API 中也是安全的.

C++ 中唯一的选择是模板。但是 C++ 模板不是安全类型化的运行时泛型,它们基本上是一种宏。因此,首先对于 C++ 中的模板,您必须将整个模板源代码提供给需要使用您的模板的任何人。此外,如果您使库 API 模板化,您将无法保证对它的调用将被编译,并且代码不会自动自我记录。

我完全同情任何其他同时使用 C# 和 C++ 并且对这一点感到沮丧的程序员。

然而,C++2X 计划添加包括范围在内的功能(这可能满足 OP?);以及概念(解决模板的弱/错误类型检查——Bjarne Stroustrup本人承认的缺陷)和模块(这可能有助于也可能无助于减轻仅标头模板的痛苦)。

  • 让我改写一下:C++ 模板不过是宏。C++当然是强类型安全的——和C#一样。 (2认同)

And*_*erd 5

IEnumerable<T>在概念上与vector.

IEnumerable规定只进只读到对象的序列访问,无论何种容器(如果有的话)持有的对象。Avector实际上是一个容器本身。

在 C++ 中,如果您想提供对容器的访问而不提供此容器的详细信息,则约定是传入两个迭代器来表示容器的开头和结尾。

一个很好的例子是accumulate的C++ STL 定义,它可以与IEnumerable<T>.Aggregate 对比

在 C++ 中

   int GetProduct(const vector<int>& v)
   {
         // We don't provide the container, but two iterators
         return std::accumulate(v.begin(), v.end(), 1, multiplies<int>());
   }
Run Code Online (Sandbox Code Playgroud)

在 C# 中

  int GetProduct(IEnumerable<int> v)
  {
        v.Aggregate(1, (l, r) => l*r);
  }
Run Code Online (Sandbox Code Playgroud)

  • @BrainSlugs83:您的评论和反对票并没有太大帮助。问题中没有任何关于使用协程实现迭代器的语法糖,这正是 C#`yield return` 的含义。这个问题不强调实现方面或消费方面,它询问迭代器接口本身,而不是便利包装器。您对实施施加了自己的好奇心,这是很自然的,但无济于事。如果你想知道实现细节,问你自己的问题,不要劫持这个。 (2认同)