35

考虑以下代码:

#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安全地进行比较吗?

4

3 回答 3

41

令人惊讶的是,C++98 和 C++11 并没有说可以将 aiterator与 a进行比较const_iterator。这导致LWG 问题 179LWG 问题 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 类型的对象,该对象引用相同的元素,而语义没有变化。

于 2016-02-14T14:41:28.043 回答
10

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

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

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

begin()(出于相同的原因, vs的答案也是肯定的cbegin(),如同一张表中所定义。)


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

如果ab都是可解引用的,那么a == b当且仅当*a*b绑定到同一个对象

即使迭代器end()andcend()不可解引用,上面的语句也暗示operator==()必须以这样的方式定义,即使a是 const-iterator 而b不是,也可以进行比较,反之亦然,因为 24.2.5 是关于前向迭代器的一般来说,包括 const 版本和非 const 版本——这很明显,例如从 24.2.5/1 开始。这就是为什么我相信表 96 中提到可兑换性的措辞也暗示了可比性。但正如 cpplearner@ 后面的回答中所描述的,这仅在 C++14 中明确明确。

于 2016-02-14T10:55:43.167 回答
10

见§23.2.1,表 96:

X::iterator

[...]

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

可转换为X::const_iterator

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

于 2016-02-14T10:58:02.870 回答