C++允许在一个span是另一个span的子span时进行std::span::iterators之间的比较吗?

16
一般来说,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;  // undefined!
}

但是对于使用std::span::subspan()创建的子范围也适用吗?例如:
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的迭代器?例如:
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;
}

3
根据“标准是否保证”的要求,我已添加了语言律师标签。 - Brian61354270
1
相关草案部分:24.7.2.2.7 / span.iterators - Brian61354270
比较不同对象上的迭代器是一个未定义行为(U.B.)。在使用 MSVC 运行此代码时,在调试模式下会产生“无法比较不兼容的 span 迭代器”的错误,但在发布模式下因为它们是原始指针所以代码可以正常运行。 - dalfaB
3
另一个有趣的问题是,子范围迭代器是否可以与底层容器的迭代器进行比较。 - Ben Voigt
1个回答

3
我认为标准不允许这样做。
根据[forward.iterators]中对Cpp17ForwardIterator的描述:
引用:

前向迭代器的==的定义域是相同底层序列上的迭代器。

这意味着两个迭代器的相等和不相等操作仅在相同的底层序列上定义。
在你的例子中,被比较的两个迭代器的底层序列是不同的,因为它们具有不同的起始点和不同的大小,这使得当一个迭代器超出另一个序列的边界时,比较不再具有明确定义。
如评论中所提到的,您的示例在MSVC-STL的调试模式下会触发一个断言,这一点由维护者在#1435中进行了解释。类似的问题也在Cpp核心指南中讨论过,参见#1157

std::ranges::subrange是否存在与std::span::subspan相同的问题?我不确定是否应该提出一个新问题,但有时候当我知道subrange.begin()不等于底层范围的begin()时,我想执行std::prev(subrange.begin())。这样做是否允许? - Weijun Zhou
1
我认为std::prev(subrange.begin())是可以的,因为subrange.begin()被指定返回底层的原始迭代器。对原始迭代器的操作的有效性不会改变。 - 康桓瑋
1
我认为std::prev(subrange.begin())是可以的,因为subrange.begin()被指定为返回底层的原始迭代器。对原始迭代器的操作的有效性不会改变。 - undefined
1
“underlying sequences of the two iterators being compared are different” - 需要引用。这可能是指span的底层序列仍然是原始缓冲区,而span本身的begin/end则是一个子序列。这取决于标准的措辞方式。 - Yakk - Adam Nevraumont
1
@Yakk-AdamNevraumont 我认为当前的标准并没有明确定义迭代器的底层序列是什么。在C++17中有一个定义:"如果且仅当通过对表达式 ++i 进行有限次应用可以使得 i == j,迭代器 j 就称为从迭代器 i 可达。如果 j 是从 i 可达的,则它们引用同一序列的元素",但我认为在当前的上下文中,它仍可能被解释得不同。 - 康桓瑋

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