C++ std :: vector <> :: iterator不是指针,为什么?

dim*_*imm 21 c++ iterator vector stdvector

只需一点介绍,简单的单词.在C++中,迭代器是"事物",你可以在其上至少编写解引用运算符*it,增量运算符++it,对于更高级的双向迭代器,减量--it,最后但并非最不重要的是,对于随机访问迭代器,我们需要运算符索引it[]和可能的加法和减法.

C++中的这类"东西"是具有相应运算符重载的类型的对象,或简单和简单的指针.

std::vector<>是一个包装连续数组的容器类,因此指针作为迭代器是有意义的.在网上,在一些文献中你可以找到vector.begin()用作指针.

使用指针的基本原理是开销更少,性能更高,特别是如果优化编译器检测到迭代并执行其操作(向量指令和内容).使用迭代器可能更难以使编译器进行优化.

知道这一点,我的问题是为什么现代STL实现,比如Mingw 4.7中的MSVC++ 2013或libstdc ++,为矢量迭代器使用了一个特殊的类?

Wal*_*ter 21

vector::iterator可以通过一个简单的指针实现完全正确(参见这里) - 实际上迭代器的概念是基于指向数组元素的指针.对于其他容器,如map,listdeque,然而,指针不会在所有的工作.那为什么不这样做呢?以下是为什么类实现比原始指针更可取的三个原因.

  1. 将迭代器实现为单独的类型允许额外的功能(超出标准所要求的范围),例如(在Quentins注释之后的编辑中添加)在解除引用迭代器时添加断言的可能性,例如,在调试模式下.

  2. 重载决策如果迭代器是一个指针T*,它可以作为有效参数传递给函数T*,而迭代器类型是不可能的.因此,制作std::vector<>::iterator指针实际上会改变现有代码的行为.例如,考虑一下

    template<typename It>
    void foo(It begin, It end);
    void foo(const double*a, const double*b, size_t n=0);
    
    std::vector<double> vec;
    foo(vec.begin(), vec.end());    // which foo is called?
    
    Run Code Online (Sandbox Code Playgroud)
  3. 依赖于参数的查找(ADL;由juanchopanza指出)如果进行非限定调用,ADL确保namespace std仅在参数是在中定义的类型时才搜索函数namespace std.所以,

    std::vector<double> vec;
    sort(vec.begin(), vec.end());             // calls std::sort
    sort(vec.data(), vec.data()+vec.size());  // fails to compile
    
    Run Code Online (Sandbox Code Playgroud)

    std::sort如果vector<>::iterator只是指针,则找不到.

  • 过载分辨率可能是最大的原因,+1 (5认同)
  • 如果调试配置使用检查迭代器(类类型)而发布配置不使用,重载解析可能会更糟。 (2认同)

Nia*_*all 7

迭代器的实现实现定义的,只要满足标准的要求即可.它可能是一个指针vector,可以工作.不使用指针有几个原因;

  • 与其他容器的一致性.
  • 调试和错误检查支持
  • 重载决策,基于类的迭代器允许重载将它们与普通指针区分开来

如果所有迭代器都是指针,则++ita map不会将其递增到下一个元素,因为内存不需要是不连续的.过去std:::vector大多数标准容器的连续内存需要"更智能"的指针 - 因此迭代器.

迭代器的物理要求dove-tail非常好,逻辑要求元素之间的移动是一个定义好的"成语"迭代它们,而不仅仅是移动到下一个存储位置.

这是STL的原始设计要求和目标之一; 容器之间的正交关系,算法和通过迭代器连接两者.

既然它们是类,您可以添加一大堆错误检查和完整性检查来调试代码(然后删除它以获得更优化的发布代码).


鉴于基于类的迭代器带来的积极方面,为什么应该或者不应该只使用std::vector迭代器指针- 一致性.早期的实现std::vector确实使用了普通指针,你可以使用它们vector.一旦你必须使用其他迭代器的类,给定它们带来的积极因素,应用它vector成为一个好主意.

  • @MarcusMüller不,指针有iterator_traits.它有效,AFAIK指针是随机访问迭代器的有效实现(除非在C++ 11中有所改变). (5认同)
  • OP确实明确询问了`vector :: iterator`,而不是`map :: iterator`. (3认同)
  • @MarcusMüller **你没有明白**。你说的没有回答问题。问题不是*为什么不为迭代器使用指针?*,而是*当指针满足所有要求时,为什么`vector::iterator`的实现方式与指针不同?*([RandomAccessIterator](http:// /en.cppreference.com/w/cpp/concept/RandomAccessIterator)) (3认同)
  • @沃尔特。一致性。`std::vector` 的早期实现确实使用了普通指针,你可以将它们用于 `vector`。一旦您必须将类用于其他迭代器,考虑到它们带来的积极影响,将其应用于 `vector` 成为一个好主意。 (2认同)

Mar*_*ler 1

因为 STL 的设计理念是,您可以编写一些在迭代器上进行迭代的内容,无论该迭代器是否只是相当于指向内存连续数组元素的指针(如std::arraystd::vector)或类似链接列表、一组密钥,在访问时动态生成的东西等。

另外,不要被愚弄:在向量情况下,取消引用可能(没有调试选项)只是分解为内联指针取消引用,因此编译后甚至不会有开销!

  • 我不明白这如何回答这个问题。您可以使用指针同样很好地完成所有这些(*迭代*)。对于许多容器来说,迭代器不能是普通指针,但“std::vector”实际上是可能的。 (5认同)
  • @MarcusMüller 好吧,你不是在这里提出这个论点。 (3认同)
  • @MarcusMüller 这仍然没有回答问题。指针可以很好地用作随机访问迭代器。实现可以很好地使用指针,我能想到的唯一会改变的是 ADL 不起作用,除非实现可以在“std”命名空间内定义指针类型。但据我所知,没有任何内容表明指针“不能”使用。 (2认同)
  • 我很确定人们可以提出一个很好的论点,即它暗示普通指针很好,因为它满足前向迭代器的所有要求。您的第一段没有解释为什么“std::vector::iterator”不是最新流行实现的指针。正如已经提到的,它没有回答这个问题。 (2认同)