const_iterator与迭代器的比较是否定义良好

Is comparison of const_iterator with iterator well-defined?

本文关键字:是否 定义 比较 iterator 迭代器 const      更新时间:2023-10-16

请考虑以下代码:

#include <vector>
#include <iostream>
int main()
{
    std::vector<int> vec{1,2,3,5};
    for(auto it=vec.cbegin();it!=vec.cend();++it)
    {
        std::cout << *it;
        // A typo: end instead of cend
        if(next(it)!=vec.end()) std::cout << ",";
    }
    std::cout << "n";
}

在这里,我引入了一个错字:在比较中,我称vec.end()而不是vec.cend()。这似乎与 gcc 5.2 的预期相同。但它实际上是否根据标准进行了明确定义?iteratorconst_iterator可以安全地进行比较吗?

令人惊讶的是,C++98 和 C++11 并没有说您可以将iteratorconst_iterator进行比较。这导致了 LWG 问题 179 和 LWG 问题 2263。现在在第 C++14 中,§ 23.2.1[container.requirements.general]p7 明确允许这样做

在表达式中

i == j
i != j
i < j
i <= j
i >= j
i > j
i - j

其中ij表示容器iterator类型的对象,或者 两者都可以用容器const_iterator的对象代替 类型引用语义没有变化的同一元素。

C++11 标准中的表 96 在第 23.2.1 节中定义了任何容器类型X(包括std::vector(的 a.cend() 的操作语义,如下所示:

const_cast<X const &>(a).end()

所以答案是肯定的,因为根据这个定义,cend()引用容器中与end()相同的元素/位置,并且X::iterator必须可转换为X::const_iterator(在同一表中也指定了这一要求(&ast;((。

(对于begin()cbegin()的原因与同一表中定义的原因相同。


(&ast;( 在其他答案的评论中已经指出,可转换性并不一定意味着比较操作i1==i2总是有效的,例如,如果operator==()是迭代器类型的成员函数,则隐式转换将只接受右侧参数,而不接受左侧参数。 24.2.5/6 状态(关于前向迭代器 ab(:

如果 ab 都是可取消引用的,则当且仅当 *a*b绑定到同一对象时a == b

尽管迭代器end()cend()是不可取消引用的,但上面的陈述意味着operator==()必须以这样一种方式定义,即使a是一个常量迭代器,而b不是,也可以进行比较,反之亦然,因为 24.2.5 是关于一般的前向迭代器,包括常量和非常量版本——这很清楚,例如从 24.2.5/1 开始。这就是为什么我确信表96中提到可兑换性的措辞也意味着可比性。但正如cpplearner@后来的回答所描述的那样,这一点直到C++14中才明确说明。

参见 §23.2.1, 表 96:

X::iterator

[...]

满足前向迭代器要求的任何迭代器类别。

可转换为X::const_iterator

所以,是的,它是明确定义的。