const迭代器和非const迭代器之间的比较,它们有效吗?

18

我有一个指向容器的迭代器,一个是const类型,另一个不是。将它们进行比较来查看它们是否都指向容器中的同一对象是否会出现问题?这是一个关于C++11迭代器的普遍问题:

可以合法比较const和非const迭代器以查看它们是否都引用相同的对象吗?无论容器类型如何(即它们都是保证引用同一容器或该容器的end()处的对象的迭代器,但一个是const而另一个不是)。

例如,请考虑以下代码:

some_c++11_container container;

// Populate container
...

some_c++11_container::iterator iObject1=container.begin();
some_c++11_container::const_iterator ciObject2=container.cbegin();

// Some operations that move iObject1 and ciObject2 around the container
...

if (ciObject2==iObject1) // Is this comparison allowed by the C++11 standard?
  ...; //Perform some action contingent on the equality of the two iterators

当然是有效的。但请注意,您正在比较迭代器,而不是迭代器指向的值。 - dchhetri
1
@user814628 请提供证据? - BoBTFish
我认为这很明显。如果我们可以比较非const和const对象,那么我们就没有理由不能进行迭代器比较。 - dchhetri
4
some_container::iteratorsome_container::const_iterator 可能是不同的类型。虽然它们模拟了 T*T const *,但它们实际上并不是这样。这就是为什么这是一个好问题。你可能很难找到一个不能正常工作的例子,但这并不意味着标准保证它会工作。除了现在有几个答案表明它确实可以工作,但这并不是我认为显而易见/平凡的。 - BoBTFish
你知道两个迭代器都指向同一个容器中的元素吗?从问题中并不清楚,因为你在示例中省略了初始化。 - Adrian McCarthy
@Adrian,我已经更新了这个例子,以便在这一点上更清楚。 - Michael Goldshteyn
4个回答

14

是的,这会像您期望的那样工作。

标准保证,对于任何容器类型,some_container::iterator 可以被隐式转换为 some_container::const_iterator

在 23.2.1 [container.requirements.general] 中定义了 X 作为包含类型为 T 的对象的容器类型,并在第一个表中列出:

表达式:X::iterator

返回类型:值类型为 T 的迭代器类型

注意:任何符合前向迭代器要求的迭代器类别, 可以转换为 X::const_iterator


表达式:X::const_iterator

返回类型:值类型为 T 的常量迭代器类型

注意:任何符合前向迭代器要求的迭代器类别。

(这些实际上不是表达式,而是类型,而不是具有“返回类型”,但它们被挤入到主要是表达式的表格中。)

因此,当您拥有 ciObject2==iObject1 时,编译器会注意到最佳的 operator==ciObject2==some_container::const_iterator(iObject1)。而在两个 const_iterator 上的 operator== 会告诉您它们是否引用相同的元素。

(我没有看到任何明确说明此转换的结果引用与原始迭代器相同的对象。我想那是默认理解的。)


1
如果你能引用具体的章节和段落,我会考虑问题已经得到回答,并给你打上勾。 - Michael Goldshteyn
5
请注意,这不是对容器的要求,而是针对标准中定义的容器的接口规范。因此,它不适用于所有迭代器,仅适用于来自标准容器的迭代器。对于其他迭代器来源(无论是容器还是其他方式),请查阅相关文档。 - Pete Becker

2

来自§24.2.3/1

如果类或指针类型X满足值类型T的输入迭代器的要求,则X满足Iterator(24.2.2)和EqualityComparable表17)要求...

因此,输入迭代器需要满足EqualityComparable

所有标准库容器迭代器都必须满足前向迭代器的要求(§23.2.1 - 表96)。由于这些要求是输入迭代器要求的超集,因此这些迭代器必须满足EqualityComparable概念。

另外,根据§23.2.1 - 表96X::iterator需要可以转换为X::const_iterator

综上所述,答案是标准确实要求将Container::const_iteratorContainer::iterator进行比较是有定义的(只要两者都是指向同一容器的有效迭代器)。


我尝试在C++20草案N4713中查找此内容。所有“迭代器可转换为”出现的地方都只是注释,即它们并不具有约束力。有两个发现:第763页上的§26.2.6(6)和第772页上的§26.2.7(8)。 - Thomas Weller

1
我认为比较它们不可能出现问题。如果您在检查 const 迭代器是否结束时,返回的迭代器 end() 不是 const。

我注意到end()的返回类型是尾置返回类型,auto会保留const吗? - aaronman
Container::end() 成员函数有两个重载,一个返回 const 迭代器,另一个则是非 const 的。 - Praetorian
3
我一直认为cbegin()和cend()是用来帮助自动类型推断的。例如:auto ciObject=myContainer.cbegin(); // ciObject是一个指向容器内对象的const迭代器 - Michael Goldshteyn

1
据我所知,iterator会隐式转换为const_iterator。并且结果必须指向相同的位置。
如果是这样的话,混合比较将进行转换,然后比较现在兼容的const_iterator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接