C++中是否允许比较std::span及其子span的迭代器?
std::span迭代器跨实例比较的合法性问题
C++中通常禁止比较不同容器的迭代器,例如以下代码的行为是未定义的:
int main() { std::vector<int> v = {1, 2, 3}; std::vector<int> w = {4, 5, 6}; std::cout << v.end() == w.end() << std::endl; // 未定义行为! }
但对于通过std::span::subspan()创建的子span,情况是否相同?比如这段代码:
int main() { int a[4] = { 1, 2, 3, 4}; std::span<int> s(a); std::span<int> t = s.subspan(1, 2); std::cout << t.begin() - s.begin() << std::endl; }
它的输出是1,符合预期——因为span的迭代器本质上可能是指向底层数组的指针,但C++标准是否明确保证该操作的正确性?
更进一步:是否可以比较来自同一连续内存对象的任意span的迭代器?例如:
int main() { int a[5] = { 1, 2, 3, 4, 5}; std::span<int> s(a); std::cout << (s.subspan(1, 1).end() < s.subspan(3, 1).begin()) << std::endl; }
答案:合法,标准明确保证
std::span的迭代器属于随机访问迭代器,其行为等价于指向底层连续内存的指针。根据C++标准规定:
- 当两个span的迭代器指向同一连续内存块(即底层存储为同一个对象/数组的一部分)时,迭代器之间的比较(
==、!=、<、>等)和算术运算(+、-)均为合法且有定义的操作。 - 通过
subspan创建的子span必然共享原span的底层连续内存,因此这些子span与原span的迭代器之间的操作完全合规。
针对你给出的例子:
- 第一个例子中,
t.begin() - s.begin()得到1是标准保证的结果,因为两个迭代器指向同一数组a的元素,算术运算的结果就是它们在内存中的偏移量差值。 - 第二个例子中,
s.subspan(1,1).end()指向a[2],s.subspan(3,1).begin()指向a[3],前者内存地址小于后者,因此比较结果为true(输出1),该行为同样受标准保障。
需要注意:仅当迭代器指向同一连续内存区域时,这类操作才合法。若两个span的底层存储是完全独立的内存块,它们的迭代器比较/运算仍属于未定义行为,和普通容器迭代器的规则一致。
内容的提问来源于stack exchange,提问作者Maks Verver
相关产品推荐
相关产品推荐

