const_iterator与迭代器的比较是否定义良好
Is comparison of const_iterator with iterator well-defined?
请考虑以下代码:
#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 的预期相同。但它实际上是否根据标准进行了明确定义?iterator
和const_iterator
可以安全地进行比较吗?
令人惊讶的是,C++98 和 C++11 并没有说您可以将iterator
与const_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
其中
i
和j
表示容器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
(在同一表中也指定了这一要求(*((。
(对于begin()
与 cbegin()
的原因与同一表中定义的原因相同。
(*( 在其他答案的评论中已经指出,可转换性并不一定意味着比较操作i1==i2
总是有效的,例如,如果operator==()
是迭代器类型的成员函数,则隐式转换将只接受右侧参数,而不接受左侧参数。 24.2.5/6 状态(关于前向迭代器 a
和 b
(:
如果
a
和b
都是可取消引用的,则当且仅当*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
所以,是的,它是明确定义的。
- 在提升multi_index容器中,是否定义了"default index"?
- sprintf(b, "%" ) 是否定义了?
- 重载虚拟行为是否定义良好?
- 它是否定义了哪些算法可以接受可变 lambda 的实现?
- 如何检查是否定义了固定宽度的整数
- &*NULL 在C++中是否定义良好?
- C++标准是否定义了结构中成员函数的函数内定义是否必须具有静态链接?
- 将以"_[a-z0-9]"开头和"using"的标识符导入全局命名空间是否定义良
- SFINAE 检测是否定义了类型
- 标准是否定义了共同基础?
- 根据是否定义了另一个宏来评估宏
- 获取未初始化对象成员的地址是否定义良好?
- 如何测试是否定义了结构的成员 (c++)
- 如何测试标准库中是否定义了 std::remove_cvref
- 对非活跃的工会成员使用"std::addressof"是否定义明确
- C++模板 - 几个专业 - 行为是否定义?
- 如何检查是否定义了标头保护
- C++14 是否定义了无符号 int 的填充位上的按位运算符的行为
- C / C++中无符号整数的减法和比较是否定义良好?
- 在比较中使用已强制转换为另一种类型的函数指针是否定义良好?