指向同一内存的两个 s 的比较begin()
和迭代器是否定义明确?end()
std::span
#include <span>
#include <cassert>
int main() {
int arr[5]{1,2,3,4,5};
std::span s1{arr};
std::span s2{arr};
assert(s1.begin() == s2.begin());
assert(s1.end() == s2.end());
assert(s1.begin() + s1.size() == s2.end());
}
到目前为止,所有断言都传递了所有实现std::span
,但是我是否遗漏了什么,从而导致出现错误,例如 UB?
就上下文而言,如果您有一个试图用跨度隐藏其内部的类,则可能会出现这种情况。
class some_class
{
public:
std::span<int> vec_view() { return vec; }
private:
std::vector<int> vec;
};
int main() {
some_class c;
std::for_each(c.vec_view().begin(), c.vec_view().end(), [](auto&){});
}
这与当一个跨度是另一个跨度的子跨度时,C++ 是否允许在 std::span::iterators 之间进行比较?有关,但这个问题与 无关std::subspan
,而且该std::span
版本还通过了 MSVC 断言,与 的版本不同std::subspan
。
谁知道呢。
std::span::iterator
模型contiguous_iterator
([span.iterators]),因此,forward_iterator
。 [iterator.concept.forward]第2段适用:C++ 标准从未定义“底层序列”是什么。
.begin()
如果“底层序列”是我们调用的对象.end()
,那么std::span(...).begin() == std::span(...).begin()
无论怎样都是未定义的。operator*
如果“底层序列”是通过在迭代器上应用 获得的元素序列,则s1.begin() == s2.begin()
必须是true
。std::string_view
第一个选项对于像and 这样的非拥有范围来说,是不必要的限制std::span
。第二个选项可能会产生意想不到的后果,比如能够比较std::ranges::filter_view
使用函数指针作为谓词创建的完全不同的迭代器(相同的迭代器类型,相同的元素,但比较毫无意义)。这里需要做出一些语言设计决策,可能需要根据具体情况进行调整。正如您已经指出的那样,有一个活跃的LWG 问题 3989,在解决该问题之前,不会对该问题有明确的答案,即使对于两个
std::span
等效的 s ,而不仅仅是子跨度。我不认为这是未定义的行为。毕竟,
std::span
它只是一个类,而不是核心语言的一部分,所以我认为这应该只是未指定的行为。span::iterator 不允许保存对源 span 的引用,因此除了指向引用元素的指针之外,它只能保存两条额外的信息:span.data() 和 span.size()。
因此,只要
span1.data() == span2.data() && span1.size() == span2.size()
,就能保证不同跨度的迭代器可以进行比较。