或者如果 T 的枚举器只是列出所有元素,那么使用向量是否安全?
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>
std::begin(container)
和
std::end(container)
而不是容器成员函数。 这允许您的算法除了 STL 容器之外还可以与原始数组(没有成员函数)一起使用。 原始数组和原始指针满足容器和迭代器的所有其他要求,但只有一个例外。
我完全不同意“不需要”,只是C++和.NET标准库的设计不同而已。 IEnumerable<> 的主要特点是它是多态的,因此它使调用者能够使用他们想要的任何类(数组、列表、集合等),同时仍然提供编译时强类型,即使在库 API 中也能实现故障安全.
C++ 中唯一的选择是模板。但 C++ 模板不是安全类型的运行时泛型,它们基本上是一种宏。因此,首先使用 C++ 中的模板,您必须向需要使用您的模板的任何人提供整个模板源代码。此外,如果你让你的库 API 模板化,你就失去了保证对它的调用能够编译的能力,并且代码不会自动自记录。
Andrei Alexandrescu 对 C# 和 C++ 的这些不同方法的动机和优缺点给出了很好的解释:“同质翻译有利于统一、简单和紧凑的生成代码。[...]相反,异构翻译有利于专业化,表达能力和生成代码的速度。” (来自他的书
The D 编程语言。)因此,人们可以理解为什么像 C# 这样的语言选择同构/多态泛型,但 C++ 喜欢异构模板并允许专门的实例化(即使它不与分离)头文件和实现文件,这是另一个故事......)
C++2X计划添加包括范围在内的功能(这可能满足OP?);以及概念(解决了模板的弱/坏类型检查问题——Bjarne Stroustrup 本人承认的缺陷)和模块(这可能有助于也可能没有帮助减少仅标题模板的痛苦)。标准的 C++ 方式是传递两个迭代器: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);
通用编程!
vector
有很大不同。
IEnumerable
提供对一系列对象的仅向前
、只读访问,无论什么容器(如果有)保存对象。
vector
实际上本身就是一个容器。
在 C++ 中,如果您想提供对容器的访问而不提供该容器的详细信息,则约定是传入两个代表容器开始和结束的迭代器。一个很好的例子是
accumulate
的 C++ STL 定义,它可以与IEnumerable.Aggregate 进行对比
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);
}