It does seem quite bold to provide these operators as such wide-ranging templates, which match all ranges. Not necessarily wrong, but it makes me slightly nervous of unintended side-effects!
I do think we should at least be constraining to accept only input ranges, as std::equal()
and std::lexicographical_compare_three_way()
require.
I don't think we need to be writing the return types, as plain auto
is fine here - both std::equal()
and std::lexicographical_compare_three_way()
return the types we want. Not doing so avoids us needing to write unqualified begin
and end
- we can make the using
s local to the functions, rather than polluting global namespace.
If we're providing ==
, we should also provide !=
. We can't use = default
, because it's a template, and if we leave it undefined, it will be synthesised from <=>
rather than from ==
.
Modified code
#include <algorithm>
#include <ranges>
inline constexpr auto operator==(const std::ranges::input_range auto& range1,
const std::ranges::input_range auto& range2)
{
using std::ranges::begin;
using std::ranges::end;
return std::equal(begin(range1), end(range1),
begin(range2), end(range2));
}
inline constexpr auto operator!=(const std::ranges::input_range auto& range1,
const std::ranges::input_range auto& range2)
{
return !(range1 == range2);
}
inline constexpr auto operator<=>(const std::ranges::input_range auto& range1,
const std::ranges::input_range auto& range2)
{
using std::ranges::begin;
using std::ranges::end;
return std::lexicographical_compare_three_way(begin(range1), end(range1),
begin(range2), end(range2));
}
#include <array>
#include <vector>
int main()
{
int const a[] = { 1, 2, 3 };
auto const v = std::vector{ 1, 2, 3 };
auto const vr = std::vector{ 3, 2, 1 };
return false
| ((a | std::views::all) <=> a) != std::strong_ordering::equivalent
| (a | std::views::reverse) != ( v | std::views::reverse)
| (v | std::views::reverse) != vr
| !(a == v)
| (a <=> v) != std::strong_ordering::equivalent
| v < a
| v > a
;
}
Answers to questions
The functions seem reasonable, though std::ranges::equal()
might make a better choice for the first (there's no std::ranges::lexicographical_compare_three_way
yet, due to the algorithm arriving at the same time as Ranges).
We can't default the other comparison operators because they are templates. We could write them all, but I think it's better to let the compiler default them.
Don't inhibit ADL by calling std::ranges::begin
specifically - have using
within the function as shown. (I might be wrong here, as niebloids have special ADL behaviour).
Yes, declare a namespace, but don't using namespace
unless the comparisons are the only things in that namespace. Client code should just alias the specific names needed, and in the smallest reasonable scope (e.g. using my_comparators::operator<=>;
). Enclosing in a namespace allows applications greater control over which parts of their code will match these very broad templates.
The functions should be detected by ADL - unless the view types you are using are not in that namespace (e.g. the standard view adapters in std::ranges::views
). If you can create a minimal example of ADL failing for non-niebloid types in the same namespace, that would make a good question for Stack Overflow.
std::valarray
which does element-wise comparisons. But I'm not sure I can distinguish a range originated from valarray and that originated from vector. If you have any ideas, please tell me. \$\endgroup\$*
operator for vectors and asked, is this code intuitive and obvious? How many of you think it means dot product? How many of you think it’s element-wise multiplication? Whenever he asks, half the people think it’s one, half the other, but they all think it’s obvious. Same with+
meaning element-wise addition or concatenation. \$\endgroup\$valarray
does, to allow maximum SIMD optimization. However, you could derive a subclass ofstd::span
orstd::ranges::subrange
that implements element-wise operations, and document, these operators are element-wise. You would probably want to implement them using expression templates. \$\endgroup\$