C# 中是否有相当于 IEnumerable<T> 的标准 C++?

问题描述 投票:0回答:4

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

c# c++ ienumerable
4个回答
61
投票

C++ 中不需要它,原因如下:

C#仅支持动态多态。 因此,要创建可重用的算法,您需要一个所有迭代器都将实现的接口。 那是

IEnumerator<T>
IEnumerable<T>
是一个返回迭代器的工厂。

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

C++ 容器和迭代器具有隐式接口,相当于 .NET

IEnumerable<T>

IEnumerator<T>
ICollection<T>
IList<T>
,即:

对于容器:

  • iterator
    const_iterator
     typedefs
  • 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 容器之外还可以与原始数组(没有成员函数)一起使用。  原始数组和原始指针满足容器和迭代器的所有其他要求,但只有一个例外。


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

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

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

Andrei Alexandrescu 对 C# 和 C++ 的这些不同方法的动机和优缺点给出了很好的解释:“同质翻译有利于统一、简单和紧凑的生成代码。[...]相反,异构翻译有利于专业化,表达能力和生成代码的速度。” (来自他的书

The D 编程语言。)因此,人们可以理解为什么像 C# 这样的语言选择同构/多态泛型,但 C++ 喜欢异构模板并允许专门的实例化(即使它不与分离)头文件和实现文件,这是另一个故事......)

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

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

12
投票
template<typename ForwardIterator> void some_function(ForwardIterator begin, ForwardIterator end) { for (; begin != end; ++begin) { do_something_with(*begin); } }

客户端代码示例:

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

通用编程!
    


7
投票
在概念上与

vector

 有很大不同。
IEnumerable

提供对一系列对象的

仅向前

只读访问,无论什么容器(如果有)保存对象。 vector 实际上本身就是一个容器。
在 C++ 中,如果您想提供对容器的访问而不提供该容器的详细信息,则约定是传入两个代表容器开始和结束的迭代器。

一个很好的例子是

accumulate

的 C++ STL 定义,它可以与

IEnumerable.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>()); }

在 C# 中

int GetProduct(IEnumerable<int> v) { v.Aggregate(1, (l, r) => l*r); }


© www.soinside.com 2019 - 2024. All rights reserved.