【问题标题】:Is comparison of const_iterator with iterator well-defined?const_iterator 与迭代器的比较是否定义明确?
【发布时间】:2016-05-25 06:27:46
【问题描述】:

考虑以下代码:

#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++ iterator comparison-operators const-iterator


    【解决方案1】:

    令人惊讶的是,C++98 和 C++11 并没有说可以将 iteratorconst_iterator 进行比较。这导致LWG issue 179LWG issue 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 的对象替换 type 引用相同的元素,语义没有变化。

    【讨论】:

    • 这仅意味着来自与Container 概念匹配的同一对象的 const 和非 const 迭代器可以安全地进行比较。但是你不能对其他迭代器做出这样的假设。
    【解决方案2】:

    参见第 23.2.1 节,表 96:

    X::iterator
    

    [...]

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

    可转换为X::const_iterator

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

    【讨论】:

    • Convertible 并不意味着可比
    • 例如,可转换不排除实现为成员函数的比较运算符,这意味着i &lt; ci 解析为i.operator &lt;(ci),其中不考虑i 的转换。标准中可能有额外的保证,但如果是这样,它们应该在答案中。
    • @hvd:幸运的是,在提问者的代码中,const_iterator 在比较的 LHS 上,plain iterator 在右边。所以它被转换了。或者我想我们可以说这很不幸,因为这意味着编译器没有捕捉到end() 的意外使用。
    • @SteveJessop 这只是一个例子。另一个是根本不采用const_iterator 的运算符,而只是const_iterator 可以隐式转换为的运算符,需要两次用户定义的转换。第三种是模板比较运算符,其中由于const_iterator/iterator 不匹配而无法推断出类型参数。 (幸运的是,现在有一个答案显示了标准中的附加要求。)
    【解决方案3】:

    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;) 在 cmets 中已指出其他答案,可兑换性并不一定意味着比较操作 i1==i2 将始终有效,例如如果operator==() 是迭代器类型的成员函数,则只接受右侧参数的隐式转换,而不接受左侧参数。 24.2.5/6 个状态(关于前向迭代器 ab):

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

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

    【讨论】:

      猜你喜欢
      • 2014-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-20
      • 2011-12-07
      • 2011-06-20
      • 2020-07-20
      相关资源
      最近更新 更多