Allow ordered comparisons of spans, document the replacement for memcmp
Instead of `memcmp(a.data(), b.data(), a.size()) < 0` allow writing `a < b` directly for two spans. This requires the types in the spans to be three-way comparable, which means if they are only comparable as `<` that the spans will not be comparable as `<`. This covers the majority of use cases of spans however. If needed in the future, we can add operators for <, <=, >, >= (and !=) like exist for optional. R=tsepez@chromium.org Bug: 40285824 Change-Id: I262764727cceb090cb420aaecabfef5d250bc56c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5594370 Commit-Queue: danakj <danakj@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org> Cr-Commit-Position: refs/heads/main@{#1310686}
This commit is contained in:
@ -103,7 +103,12 @@ constexpr size_t must_not_be_dynamic_extent() {
|
||||
template <class T, class U, size_t N, size_t M>
|
||||
requires((N == M || N == dynamic_extent || M == dynamic_extent) &&
|
||||
std::equality_comparable_with<T, U>)
|
||||
constexpr bool span_cmp(span<T, N> l, span<U, M> r);
|
||||
constexpr bool span_eq(span<T, N> l, span<U, M> r);
|
||||
template <class T, class U, size_t N, size_t M>
|
||||
requires((N == M || N == dynamic_extent || M == dynamic_extent) &&
|
||||
std::three_way_comparable_with<T, U>)
|
||||
constexpr auto span_cmp(span<T, N> l, span<U, M> r)
|
||||
-> decltype(l[0u] <=> r[0u]);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
@ -235,6 +240,7 @@ constexpr bool span_cmp(span<T, N> l, span<U, M> r);
|
||||
// - byte_span_from_cstring() function.
|
||||
// - split_at() method.
|
||||
// - operator==() comparator function.
|
||||
// - operator<=>() comparator function.
|
||||
//
|
||||
// Furthermore, all constructors and methods are marked noexcept due to the lack
|
||||
// of exceptions in Chromium.
|
||||
@ -632,17 +638,48 @@ class GSL_POINTER span {
|
||||
friend constexpr bool operator==(span lhs, span rhs)
|
||||
requires(std::is_const_v<T> && std::equality_comparable<T>)
|
||||
{
|
||||
return internal::span_cmp(span<const T, N>(lhs), span<const T, N>(rhs));
|
||||
return internal::span_eq(span<const T, N>(lhs), span<const T, N>(rhs));
|
||||
}
|
||||
friend constexpr bool operator==(span lhs, span<const T, N> rhs)
|
||||
requires(!std::is_const_v<T> && std::equality_comparable<const T>)
|
||||
{
|
||||
return internal::span_cmp(span<const T, N>(lhs), span<const T, N>(rhs));
|
||||
return internal::span_eq(span<const T, N>(lhs), span<const T, N>(rhs));
|
||||
}
|
||||
template <class U, size_t M>
|
||||
requires((N == M || M == dynamic_extent) &&
|
||||
std::equality_comparable_with<const T, const U>)
|
||||
friend constexpr bool operator==(span lhs, span<U, M> rhs) {
|
||||
return internal::span_eq(span<const T, N>(lhs), span<const U, M>(rhs));
|
||||
}
|
||||
|
||||
// Compares two spans for ordering by comparing the objects pointed to by the
|
||||
// spans. The operation is defined for spans of different types as long as the
|
||||
// types are themselves ordered via `<=>`.
|
||||
//
|
||||
// For primitive types, this replaces the less safe `memcmp` function, where
|
||||
// `memcmp(a.data(), b.data(), a.size()) < 0` can be written as `a < b` and
|
||||
// can no longer go outside the bounds of `b`.
|
||||
//
|
||||
// If both spans are empty, they are always equal (even though their data
|
||||
// pointers may differ).
|
||||
//
|
||||
// # Implementation note
|
||||
// The non-template overloads allow implicit conversions to span for
|
||||
// comparison.
|
||||
friend constexpr auto operator<=>(span lhs, span rhs)
|
||||
requires(std::is_const_v<T> && std::three_way_comparable<T>)
|
||||
{
|
||||
return internal::span_cmp(span<const T, N>(lhs), span<const T, N>(rhs));
|
||||
}
|
||||
friend constexpr auto operator<=>(span lhs, span<const T, N> rhs)
|
||||
requires(!std::is_const_v<T> && std::three_way_comparable<const T>)
|
||||
{
|
||||
return internal::span_cmp(span<const T, N>(lhs), span<const T, N>(rhs));
|
||||
}
|
||||
template <class U, size_t M>
|
||||
requires((N == M || M == dynamic_extent) &&
|
||||
std::three_way_comparable_with<const T, const U>)
|
||||
friend constexpr auto operator<=>(span lhs, span<U, M> rhs) {
|
||||
return internal::span_cmp(span<const T, N>(lhs), span<const U, M>(rhs));
|
||||
}
|
||||
|
||||
@ -1017,16 +1054,46 @@ class GSL_POINTER span<T, dynamic_extent, InternalPtrType> {
|
||||
friend constexpr bool operator==(span lhs, span rhs)
|
||||
requires(std::is_const_v<T> && std::equality_comparable<T>)
|
||||
{
|
||||
return internal::span_cmp(span<const T>(lhs), span<const T>(rhs));
|
||||
return internal::span_eq(span<const T>(lhs), span<const T>(rhs));
|
||||
}
|
||||
friend constexpr bool operator==(span lhs, span<const T> rhs)
|
||||
requires(!std::is_const_v<T> && std::equality_comparable<const T>)
|
||||
{
|
||||
return internal::span_cmp(span<const T>(lhs), span<const T>(rhs));
|
||||
return internal::span_eq(span<const T>(lhs), span<const T>(rhs));
|
||||
}
|
||||
template <class U, size_t M>
|
||||
requires(std::equality_comparable_with<const T, const U>)
|
||||
friend constexpr bool operator==(span lhs, span<U, M> rhs) {
|
||||
return internal::span_eq(span<const T>(lhs), span<const U, M>(rhs));
|
||||
}
|
||||
|
||||
// Compares two spans for ordering by comparing the objects pointed to by the
|
||||
// spans. The operation is defined for spans of different types as long as the
|
||||
// types are themselves ordered via `<=>`.
|
||||
//
|
||||
// For primitive types, this replaces the less safe `memcmp` function, where
|
||||
// `memcmp(a.data(), b.data(), a.size()) < 0` can be written as `a < b` and
|
||||
// can no longer go outside the bounds of `b`.
|
||||
//
|
||||
// If both spans are empty, they are always equal (even though their data
|
||||
// pointers may differ).
|
||||
//
|
||||
// # Implementation note
|
||||
// The non-template overloads allow implicit conversions to span for
|
||||
// comparison.
|
||||
friend constexpr auto operator<=>(span lhs, span rhs)
|
||||
requires(std::is_const_v<T> && std::three_way_comparable<T>)
|
||||
{
|
||||
return internal::span_cmp(span<const T>(lhs), span<const T>(rhs));
|
||||
}
|
||||
friend constexpr auto operator<=>(span lhs, span<const T> rhs)
|
||||
requires(!std::is_const_v<T> && std::three_way_comparable<const T>)
|
||||
{
|
||||
return internal::span_cmp(span<const T>(lhs), span<const T>(rhs));
|
||||
}
|
||||
template <class U, size_t M>
|
||||
requires(std::three_way_comparable_with<const T, const U>)
|
||||
friend constexpr auto operator<=>(span lhs, span<U, M> rhs) {
|
||||
return internal::span_cmp(span<const T>(lhs), span<const U, M>(rhs));
|
||||
}
|
||||
|
||||
@ -1390,10 +1457,20 @@ namespace internal {
|
||||
template <class T, class U, size_t N, size_t M>
|
||||
requires((N == M || N == dynamic_extent || M == dynamic_extent) &&
|
||||
std::equality_comparable_with<T, U>)
|
||||
constexpr bool span_cmp(span<T, N> l, span<U, M> r) {
|
||||
constexpr bool span_eq(span<T, N> l, span<U, M> r) {
|
||||
return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin());
|
||||
}
|
||||
|
||||
// Template helper for implementing operator<=>
|
||||
template <class T, class U, size_t N, size_t M>
|
||||
requires((N == M || N == dynamic_extent || M == dynamic_extent) &&
|
||||
std::three_way_comparable_with<T, U>)
|
||||
constexpr auto span_cmp(span<T, N> l, span<U, M> r)
|
||||
-> decltype(l[0u] <=> r[0u]) {
|
||||
return std::lexicographical_compare_three_way(l.begin(), l.end(), r.begin(),
|
||||
r.end());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace base
|
||||
|
@ -2325,6 +2325,211 @@ TEST(SpanTest, CompareEquality) {
|
||||
static_assert(span(arr2_c) == span(arr3_lc).first(2u));
|
||||
}
|
||||
|
||||
TEST(SpanTest, CompareOrdered) {
|
||||
static_assert(std::three_way_comparable<int>);
|
||||
int32_t arr2[] = {1, 2};
|
||||
int32_t arr3[] = {1, 2, 3};
|
||||
int32_t rra3[] = {3, 2, 1};
|
||||
int32_t vec3[] = {1, 2, 3};
|
||||
constexpr const int32_t arr2_c[] = {1, 2};
|
||||
constexpr const int32_t arr3_c[] = {1, 2, 3};
|
||||
constexpr const int32_t rra3_c[] = {3, 2, 1};
|
||||
|
||||
// Less than.
|
||||
EXPECT_TRUE(span(arr3) < span(rra3));
|
||||
EXPECT_TRUE(span(arr2).first(2u) < span(arr3));
|
||||
// Greater than.
|
||||
EXPECT_TRUE(span(rra3) > span(arr3));
|
||||
EXPECT_TRUE(span(arr3) > span(arr2).first(2u));
|
||||
|
||||
// Comparing empty spans that are fixed and dynamic size.
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<int32_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<int32_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<int32_t, 0u>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<int32_t, 0u>()) == 0);
|
||||
// Non-null data pointer, but both are empty.
|
||||
EXPECT_TRUE(span(arr2).first(0u) <=> span(arr2).last(0u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first<0u>() <=> span(arr2).last<0u>() == 0);
|
||||
|
||||
// Spans of different dynamic sizes.
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3).first(3u) < 0);
|
||||
// Spans of same dynamic size and same values.
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3).first(2u) == 0);
|
||||
// Spans of same dynamic size but different values.
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(rra3).first(2u) < 0);
|
||||
|
||||
// Spans of different sizes (one dynamic one fixed).
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3).first<3u>() < 0);
|
||||
// Spans of same size and same values.
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3).first<2u>() == 0);
|
||||
// Spans of same size but different values.
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(rra3).first(2u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(rra3).first<2u>() < 0);
|
||||
|
||||
// Spans of different fixed sizes do not compile (as in Rust)
|
||||
// https://godbolt.org/z/MrnbPeozr and are covered in nocompile tests.
|
||||
|
||||
// Comparing const and non-const. Same tests as above otherwise.
|
||||
|
||||
EXPECT_TRUE((span<const int32_t>() <=> span<int32_t>()) == 0);
|
||||
EXPECT_TRUE((span<const int32_t, 0u>() <=> span<int32_t>()) == 0);
|
||||
EXPECT_TRUE((span<const int32_t>() <=> span<int32_t, 0u>()) == 0);
|
||||
EXPECT_TRUE((span<const int32_t, 0u>() <=> span<int32_t, 0u>()) == 0);
|
||||
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<const int32_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<const int32_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<const int32_t, 0u>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<const int32_t, 0u>()) == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_c).first(0u) <=> span(arr2).last(0u) == 0);
|
||||
EXPECT_TRUE(span(arr2_c).first<0u>() <=> span(arr2).last<0u>() == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first(0u) <=> span(arr2_c).last(0u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first<0u>() <=> span(arr2_c).last<0u>() == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_c).first(2u) <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2_c).first(2u) <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2_c).first(2u) <=> span(rra3).first(2u) < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_c).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_c).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(rra3_c).first(2u) < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_c).first<2u>() <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2_c).first(2u) <=> span(arr3).first<3u>() < 0);
|
||||
EXPECT_TRUE(span(arr2_c).first<2u>() <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2_c).first(2u) <=> span(arr3).first<2u>() == 0);
|
||||
EXPECT_TRUE(span(arr2_c).first<2u>() <=> span(rra3).first(2u) < 0);
|
||||
EXPECT_TRUE(span(arr2_c).first(2u) <=> span(rra3).first<2u>() < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(arr3_c).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_c).first<3u>() < 0);
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(arr3_c).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_c).first<2u>() == 0);
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(rra3_c).first(2u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(rra3_c).first<2u>() < 0);
|
||||
|
||||
// Comparing different types which are comparable. Same tests as above
|
||||
// otherwise.
|
||||
|
||||
static_assert(std::three_way_comparable_with<int32_t, int64_t>);
|
||||
int64_t arr2_l[] = {1, 2};
|
||||
int64_t arr3_l[] = {1, 2, 3};
|
||||
int64_t rra3_l[] = {3, 2, 1};
|
||||
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<int64_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<int64_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<int64_t, 0u>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<int64_t, 0u>()) == 0);
|
||||
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<int64_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<int64_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<int64_t, 0u>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<int64_t, 0u>()) == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_l).first(0u) <=> span(arr2).last(0u) == 0);
|
||||
EXPECT_TRUE(span(arr2_l).first<0u>() <=> span(arr2).last<0u>() == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first(0u) <=> span(arr2_l).last(0u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first<0u>() <=> span(arr2_l).last<0u>() == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_l).first(2u) <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2_l).first(2u) <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2_l).first(2u) <=> span(rra3).first(2u) < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_l).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_l).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(rra3_l).first(2u) < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_l).first<2u>() <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2_l).first(2u) <=> span(arr3).first<3u>() < 0);
|
||||
EXPECT_TRUE(span(arr2_l).first<2u>() <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2_l).first(2u) <=> span(arr3).first<2u>() == 0);
|
||||
EXPECT_TRUE(span(arr2_l).first<2u>() <=> span(rra3).first(2u) < 0);
|
||||
EXPECT_TRUE(span(arr2_l).first(2u) <=> span(rra3).first<2u>() < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(arr3_l).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_l).first<3u>() < 0);
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(arr3_l).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_l).first<2u>() == 0);
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(rra3_l).first(2u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(rra3_l).first<2u>() < 0);
|
||||
|
||||
// Comparing different types and different const-ness at the same time.
|
||||
|
||||
constexpr const int64_t arr2_lc[] = {1, 2};
|
||||
constexpr const int64_t arr3_lc[] = {1, 2, 3};
|
||||
constexpr const int64_t rra3_lc[] = {3, 2, 1};
|
||||
|
||||
EXPECT_TRUE((span<const int32_t>() <=> span<int64_t>()) == 0);
|
||||
EXPECT_TRUE((span<const int32_t, 0u>() <=> span<int64_t>()) == 0);
|
||||
EXPECT_TRUE((span<const int32_t>() <=> span<int64_t, 0u>()) == 0);
|
||||
EXPECT_TRUE((span<const int32_t, 0u>() <=> span<int64_t, 0u>()) == 0);
|
||||
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<const int64_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<const int64_t>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t>() <=> span<const int64_t, 0u>()) == 0);
|
||||
EXPECT_TRUE((span<int32_t, 0u>() <=> span<const int64_t, 0u>()) == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_lc).first(0u) <=> span(arr2).last(0u) == 0);
|
||||
EXPECT_TRUE(span(arr2_lc).first<0u>() <=> span(arr2).last<0u>() == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first(0u) <=> span(arr2_lc).last(0u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first<0u>() <=> span(arr2_lc).last<0u>() == 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_lc).first(2u) <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2_lc).first(2u) <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2_lc).first(2u) <=> span(rra3).first(2u) < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_lc).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_lc).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(rra3_lc).first(2u) < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2_lc).first<2u>() <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2_lc).first(2u) <=> span(arr3).first<3u>() < 0);
|
||||
EXPECT_TRUE(span(arr2_lc).first<2u>() <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2_lc).first(2u) <=> span(arr3).first<2u>() == 0);
|
||||
EXPECT_TRUE(span(arr2_lc).first<2u>() <=> span(rra3).first(2u) < 0);
|
||||
EXPECT_TRUE(span(arr2_lc).first(2u) <=> span(rra3).first<2u>() < 0);
|
||||
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(arr3_lc).first(3u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_lc).first<3u>() < 0);
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(arr3_lc).first(2u) == 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(arr3_lc).first<2u>() == 0);
|
||||
EXPECT_TRUE(span(arr2).first<2u>() <=> span(rra3_lc).first(2u) < 0);
|
||||
EXPECT_TRUE(span(arr2).first(2u) <=> span(rra3_lc).first<2u>() < 0);
|
||||
|
||||
// Comparing with an implicit conversion to span. This only works if the span
|
||||
// types actually match (i.e. not for any comparable types) since otherwise
|
||||
// the type can not be deduced. Implicit conversion from mutable to const
|
||||
// can be inferred though.
|
||||
|
||||
EXPECT_TRUE(arr2 <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(arr2 <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(arr2 <=> span(rra3).first(2u) < 0);
|
||||
|
||||
EXPECT_TRUE(arr2 <=> span(arr3_c).first(3u) < 0);
|
||||
EXPECT_TRUE(arr2 <=> span(arr3_c).first(2u) == 0);
|
||||
EXPECT_TRUE(arr2 <=> span(rra3_c).first(2u) < 0);
|
||||
|
||||
EXPECT_TRUE(arr2_c <=> span(arr3).first(3u) < 0);
|
||||
EXPECT_TRUE(arr2_c <=> span(arr3).first(2u) == 0);
|
||||
EXPECT_TRUE(arr2_c <=> span(rra3).first(2u) < 0);
|
||||
|
||||
// Comparing mutable to mutable, there's no ambiguity about which overload to
|
||||
// call (mutable or implicit-const).
|
||||
EXPECT_FALSE(span(arr3) <=> rra3 == 0); // Fixed size.
|
||||
EXPECT_FALSE(span(vec3).first(2u) <=> vec3 == 0); // Dynamic size.
|
||||
EXPECT_FALSE(span(arr3).first(2u) <=> rra3 == 0); // Fixed with dynamic size.
|
||||
|
||||
// Constexpr comparison.
|
||||
static_assert(span<int>() <=> span<int, 0u>() == 0);
|
||||
static_assert(span(arr2_c) <=> span(arr3_c).first(2u) == 0);
|
||||
static_assert(span(arr2_c) <=> span(arr3_lc).first(2u) == 0);
|
||||
}
|
||||
|
||||
// These are all examples from //docs/unsafe_buffers.md, copied here to ensure
|
||||
// they compile.
|
||||
TEST(SpanTest, Example_UnsafeBuffersPatterns) {
|
||||
@ -2391,6 +2596,7 @@ TEST(SpanTest, Example_UnsafeBuffersPatterns) {
|
||||
uint8_t array1[12] = {};
|
||||
uint8_t array2[12] = {};
|
||||
[[maybe_unused]] bool eq = memcmp(array1, array2, sizeof(array1)) == 0;
|
||||
[[maybe_unused]] bool less = memcmp(array1, array2, sizeof(array1)) < 0;
|
||||
|
||||
// In tests.
|
||||
for (size_t i = 0; i < sizeof(array1); ++i) {
|
||||
@ -2404,6 +2610,7 @@ TEST(SpanTest, Example_UnsafeBuffersPatterns) {
|
||||
uint8_t array2[12] = {};
|
||||
// If one side is a span, the other will convert to span too.
|
||||
[[maybe_unused]] bool eq = base::span(array1) == array2;
|
||||
[[maybe_unused]] bool less = base::span(array1) < array2;
|
||||
|
||||
// In tests.
|
||||
EXPECT_EQ(base::span(array1), array2);
|
||||
|
@ -88,6 +88,7 @@ You have:
|
||||
uint8_t array1[12] = {};
|
||||
uint8_t array2[12] = {};
|
||||
bool eq = memcmp(array1, array2, sizeof(array1)) == 0;
|
||||
bool less = memcmp(array1, array2, sizeof(array1)) < 0;
|
||||
|
||||
// In tests.
|
||||
for (size_t i = 0; i < sizeof(array1); ++i) {
|
||||
@ -102,6 +103,7 @@ uint8_t array1[12] = {};
|
||||
uint8_t array2[12] = {};
|
||||
// If one side is a span, the other will convert to span too.
|
||||
bool eq = base::span(array1) == array2;
|
||||
bool less = base::span(array1) < array2;
|
||||
|
||||
// In tests.
|
||||
EXPECT_EQ(base::span(array1), array2);
|
||||
|
Reference in New Issue
Block a user