在vector(或其他容器)构造函数中使用数组/指针,其类型要求为Iterator.这怎么可能

C++ - Using array/pointers in vector (or other container) constructor that expects type Iterator. How is that possible?

本文关键字:类型 指针 怎么可能 Iterator 数组 其他 vector 构造函数      更新时间:2023-10-16

下面的文字来自一个c++在线课程。它说vector类的构造函数

 template <class InputIterator>
          vector ( InputIterator first, InputIterator last, const Allocator& = Allocator() );

可以接收指针作为第一个参数(InputIterator first)和第二个参数(InputIterator last)。

下一个构造函数使用迭代器初始化自身。它将用从第一个(包括)到最后一个值的副本创建一个向量(独家)。在最典型的情况下,这个构造函数创建一个new使用已存在集合中的元素的向量。但是由于迭代器被定义为一组操作,而不是有了一定的类型,也可以使用普通指针。这注释会得出一个可以使用普通c++数组得出的结论初始化集合。事实上你可以。

#include <vector>
#include <iostream>
using namespace std;
int main()
{
    int a1[]={1,2,3,4,5,6,7,8,9,10};
    //first one
    vector<int> v1(a1, a1+10);
    cout<<"Size (v1):  "<<v1.size()<<endl;
    for(unsigned i = 0; i < v1.size(); ++i)
    {
        cout<< v1[i]<<" ";
    }
    cout<<endl;
    //second one;
    vector<int> v2(a1+5,a1+10);
    cout<<"Size (v2):  "<<v2.size()<<endl;
    for(unsigned i = 0; i < v2.size(); ++i)
    {
        cout<< v2[i]<<" ";
    }
    cout<<endl;
    return 0;
}
我可以习惯这一点,但我真的不明白为什么这是可能的。我想了解一下这个技术。

在这种情况下(见代码)我的问题是我怎么可能简单地把数组地址而不是迭代器?

在上面的代码中,将类型为int *的元素作为类型为InputIterator的参数。这让我很困惑。

所有的故事都是关于构造对迭代器的期望。它期望能够增加它(使用++),减少它(使用*)。交互器就是重载++和*操作符的类,或者是自然支持这两种操作的基本指针类型。

c++迭代器实际上是精心设计的,用于泛化指针的语义并模仿它们的语法(因此有了operator*重载)。

请参阅原始SGI文档中的一般讨论或此处对最新c++版本的处理,并特别注意:

  1. 指针满足RandomAccessIterators
  2. 的所有要求
  3. RandomAccessIterators为InputIterator的需求建模的超集
  4. std::vector构造函数只需要InputIterator

如果你不相信第一点,你可能还会对标准库显式地为原始指针提供迭代器特性感兴趣,如果你还在纠结,看看构造函数的实现,并确保对其参数的所有操作对于指针来说都是格式良好的。

要回答你的最后一个问题,一个指针指向内存中的一个位置。由于内存是随机访问的,所以指针可以被归类为具有随机访问的。迭代器具有相同类型的分类。Cplusplus.com有一个很好的布局,在这里显示了哪种类型的迭代器支持什么。由此我们可以看到,输入迭代器是最低级的类型,它们可以做最少的操作。由于指针与随机访问迭代器相同,并且随机访问迭代器可以做输入迭代器所能做的一切,因此这没有任何问题。

如果是另一种情况,你有一个需要随机访问迭代器的函数,而你提供了一个前向迭代器,则不能保证代码能正常工作。这是因为该函数可能依赖于随机访问迭代器可以完成而正向迭代器无法完成的某些操作。

一般来说,当你编写这种类型的代码并指定你想要的迭代器类型时,你指定了允许你的代码编译的最小类型。打印内容只需要一个前向迭代器因为你只是遍历内容而排序需要随机访问

因为数组将其元素存储在连续的内存中。因此,如果你知道类型的大小,以及指向第一个元素的指针,你就可以计算出所有剩余元素的地址。

int values[10];
&values[5] == &values[0] + 5 // due to pointer arithmetic

§5.7加性运算符

加性运算符+和-从左到右分组。通常的算术转换是对算术或枚举类型的操作数执行的。

对于加法,要么两个操作数都是算术或无作用域枚举类型,要么一个操作数是指向完全定义的对象类型的指针,另一个操作数是整型或无作用域枚举类型。

对于减法,下列条件之一必须成立:
-两个操作数都是算术或无作用域枚举类型;或
-两个操作数都是指向相同完全定义对象类型的cv限定或cv不限定版本的指针;或
—左操作数是指向完全定义对象类型的指针,右操作数是整型或无作用域枚举类型。

当对指针进行整型表达式的加减运算时,结果具有指针操作数的类型。如果指针操作数指向数组对象的一个元素,并且数组足够大,则结果指向与原始元素的元素偏移量,使得结果与原始数组元素的下标之差等于整型表达式。换句话说,如果表达式P指向数组对象的第i个元素,则表达式(P)+N(相当于N+(P))和(P)-N(其中N的值为N)分别指向数组对象的第i + N个和第i -N个元素,前提是它们存在。此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向数组对象的最后一个元素,如果表达式Q指向数组对象的最后一个元素,则表达式(Q)-1指向数组对象的最后一个元素。如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象最后一个元素的后一个,则求值不会产生溢出;否则,行为是未定义的。

基本上,标准定义了指针T*和整型之间的算术运算

T* t + n  // where n is an offset (of integral type)

从初始t得到另一个T*,即n * sizeof(T)