【发布时间】:2011-06-07 03:35:24
【问题描述】:
比较来自不同容器的迭代器是否合法?
std::vector<int> foo;
std::vector<int> bar;
表达式foo.begin() == bar.begin() 是否产生错误或未定义的行为?
(我正在编写一个自定义迭代器,并在实现operator== 时偶然发现了这个问题。)
【问题讨论】:
标签: c++ stl comparison iterator
比较来自不同容器的迭代器是否合法?
std::vector<int> foo;
std::vector<int> bar;
表达式foo.begin() == bar.begin() 是否产生错误或未定义的行为?
(我正在编写一个自定义迭代器,并在实现operator== 时偶然发现了这个问题。)
【问题讨论】:
标签: c++ stl comparison iterator
没有。如果它是合法的,这意味着指针不会是迭代器。
【讨论】:
int a, b; &a == &b;,对吧? (尽管您在原告整数上的示例也是非法的,但出于不同的原因。)
operator = 实现将被破坏(测试自分配)。确实,指针指向数组范围之外(或后面)是非法的,但这是不同的。
&a == &b 是非法的,请参阅 Konrad 的评论。但是,a == b 是,因为读取未初始化的变量会产生未定义的行为。
您不能直接比较来自不同容器的迭代器。迭代器是一个使用容器的内部状态对其进行遍历的对象;将一个容器的内部结构与另一个容器进行比较根本没有意义。
但是,如果从container.begin() 产生的迭代器可用,则可能通过从begin() 遍历的对象数与当前迭代器值进行比较是有意义的。这是使用std::distance 完成的:
int a = std::distance(containerA.begin(), iteratorA);
int b = std::distance(containerB.begin(), iteratorB);
if (a <comparison> b)
{ /* ... */ }
没有更多上下文,很难判断这是否能解决您的问题。 YMMV。
【讨论】:
ISO/IEC 14882:2003(E) 5.10.1
==(等于)和 !=(不等于)运算符与关系运算符具有相同的语义限制、转换和结果类型,但它们的优先级较低且结果为真值。 [ .. ] 指向相同类型的对象或函数的指针(在指针转换之后)可以进行相等性比较。相同类型的两个指针比较相等当且仅当它们都为空,都指向同一个函数,或者都表示相同的地址(3.9.2)。
XCode (3.2.3) 上的模拟结果:
#include <iostream>
#include <vector>
int main()
{
std::vector <int> a,aa;
std::vector <float> b;
if( a.begin() == aa.begin() )
std::cout << "\n a.begin() == aa.begin() \n" ;
a.push_back(10) ;
if( a.begin() != aa.begin() )
std::cout << "\n After push back a.begin() != aa.begin() \n" ;
// Error if( a.begin() == b.begin() )
return 0;
}
输出:
a.begin() == aa.begin()
后推 a.begin() != aa.begin()
【讨论】:
std::list<T>::iterator不支持“指针运算”。
but need not be the other way around。谢谢。
我没有从标准的 100% 中得到对输入迭代器的要求,但是从那里(前向/双向/随机访问迭代器)对 == 的域没有要求,所以它必须 return false 导致等价关系。但是,您不能对来自不同容器的迭代器执行 或减法。
编辑:它不必返回 false,它必须产生等价关系,这允许两个空容器的 .begin() 比较相等(如另一个答案所示)。如果迭代器是可解引用的,a == b => *a == *b 必须持有。它仍然不是未定义的行为。
【讨论】:
The domain of == for forward iterators is that of iterators over the same underlying sequence. § 24.2.5 (C++0x)
== 域的降低要求仅适用于输入迭代器。对于输入迭代器,== 必须是在其域上 的等价关系,但对于前向迭代器及以上的迭代器,== 必须是等价关系...句号。
据我所知,未定义的行为。在 VS 2010 中带有
/*
* to disable iterator checking that complains that the iterators are incompatible (come from * different containers :-)
*/
#define _HAS_ITERATOR_DEBUGGING 0
std::vector<int> vec1, vec2;
std::vector<int>::iterator it1 = vec1.begin();
std::vector<int>::iterator it2 = vec2.begin();
if (it1 == it2)
{
std::cout << "they are equal!!!";
}
在这种情况下,相等性测试返回 true :-),因为容器是空的,并且迭代器的 _Ptr 成员都是 nullptr。
谁知道你的实现可能会做不同的事情,测试会返回 false :-)。
编辑:
参见C++ Standard library Active Issues list“446. 不同容器之间的迭代器相等”。也许有人可以检查标准以查看更改是否被采用?
可能不是,因为它在活动问题列表中,所以也回答了这个问题的 Charles Bailey 是正确的,它是未指定的行为。
所以我猜想不同实现之间的行为可能会有所不同(至少在理论上),这只是一个问题。
事实上,在 STL 实现中启用了迭代器调试,并带有 VS 检查,对于这种确切的情况(迭代器来自不同的容器),至少对我来说再次发出信号,应该尽可能避免进行这种比较。
我相信这是未指定的行为 (C++03)。 std::vector 迭代器是随机访问迭代器,== 的行为在前向迭代器的要求中定义。
== 是等价关系
请注意,这是对类型的要求,因此必须(在这种情况下)适用于任何一对有效(可取消引用或其他)std::vector::iterators。我相信这意味着==必须给你一个true/false的答案并且不能引起UB。
——如果 a 和 b 相等,则 a 和 b 都可以取消引用,否则都不能取消引用。
相反,可取消引用的迭代器无法与不可取消引用的迭代器进行比较。
——如果 a 和 b 都是可解引用的,那么当且仅当 *a 和 *b 是同一个对象时 a == b。
请注意,对于两个不可取消引用的迭代器,是否没有a == b 的要求。只要== 是传递的(如果a.end() == b.end() 和b.end() == c.end() 然后a.end() == c.end()),自反(a.end() == a.end())和对称的(如果a.end() == b.end() 然后b.end() == a.end())它无关紧要如果一些,所有或者没有 end() 不同容器的迭代器比较相等。
另外请注意,这与< 形成对比。 < 是根据b - a 定义的,其中a 和b 都是随机访问迭代器。执行b - a 的前提条件是必须有一个Distance 值n 使得a + n == b 要求a 和b 成为同一范围内的迭代器。
【讨论】:
a == b THEN *a == *b,但反过来在一般情况下不成立。
*a == *b。
如果考虑 C++11 标准 (n3337):
§ 24.2.1 — [iterator.requirements.general#6]
迭代器
j被称为可从迭代器i到达当且仅当表达式++i的应用程序的有限序列使得i == j。如果从i可以访问j,则它们指的是相同序列的元素。
§ 24.2.5 — [forward.iterators#2]
前向迭代器的
==的域是相同底层序列上的迭代器的域。
鉴于RandomAccessIterator 必须满足ForwardIterator 提出的所有要求,比较来自不同容器的迭代器是未定义的。
LWG issue #446专门讨论了这个问题,建议在标准中添加以下文字(感谢@Lightness Races in Orbit引起注意):
直接或间接评估任何比较函数或二元运算符的结果,其中两个迭代器值作为参数,这些迭代器值是从两个不同的范围 r1 和 r2(包括它们的过去值)不是一个公共范围的子范围是未定义的,除非另有明确说明。
【讨论】: