Example Program and Compiler Errors
The following code produces two compiler errors (MSVC++ 2022 compiling with /std:c++latest because <ranges> isn't yet enabled for /std:c++20 at time of writing this question):
error C2672: 'operator __surrogate_func': no matching overloaded function found
error C7602: 'std::ranges::_Equal_fn::operator ()': the associated constraints are not satisfied
#include <iostream>
#include <ranges>
#include <algorithm>
#include <string>
#include <cctype>
int main() {
using std::views::transform;
std::vector<std::string> foo{ "THe", "CaT", "SaT", "oN", "THe", "MaT" };
std::vector<std::string> bar{ "The", "Cat", "Sat", "On", "The", "Mat" };
bool are_equal = std::ranges::equal(
foo | transform(transform(std::tolower)),
bar | transform(transform(std::tolower)),
std::ranges::equal
);
std::cout
<< "vectors 'foo' and 'bar' are equal? - "
<< std::boolalpha << are_equal << std::endl;
}
Analysis
My guess regarding the first error __surrogate_func appears to be attempting to use operator== between the two transform_view::iterators
(As far as I can tell, the iterator defines operator==).
The second error appears to relate to a constraint for std::ranges::equal::operator().
As far as I can tell, a constraint does not seem to be satisfied by transform_view, or perhaps more specifically, transform_view::iterator.
Looking at the source for ..\include\algorithm, ranges::equal is an instance of class _Equal_fn, with _Equal_fn::operator() having constraint
requires indirectly_comparable<iterator_t<_Rng1>, iterator_t<_Rng2>.
I'm struggling to find anything on cppreference/ranges/transform_view to suggest that the transform_view::iterator is required to conform to this constraint, so I assume it's not supposed to.
However the implementation _Equal_fn clearly behaves as-expected when bypassed using a lambda without any constraints to wrap ranges::equal. This alternative implementation compiles in MSVC++ and produces the expected result:
auto lambda_equal = [](const auto& lhs, const auto& rhs) {
return std::ranges::equal(lhs, rhs);
};
// Now it compiles (MSVC++)
bool are_equal = std::ranges::equal(
foo | transform(transform(std::tolower)),
bar | transform(transform(std::tolower)),
lambda_equal
);
(Which could be made a little more generic using a python-esque decorator pattern to make it generic enough for any other binary functionoid):
constexpr auto binary_invocable = [](const auto& fn) {
return [&fn](const auto& lhs, const auto& rhs) {
return fn(lhs, rhs);
};
};
// Now it compiles (MSVC++)
bool are_equal = std::ranges::equal(
foo | transform(transform(std::tolower)),
bar | transform(transform(std::tolower)),
binary_invocable(std::ranges::equal)
);
Question:
On the basis that the problem appears to be with the constraint and not a lack of an iterator::operator== (), is there any way, using standard library functions/functors to either wrap range::equal or to wrap transform_view, as a means to satisfying the indirectly_comparable<iterator_t<Rng1>, iterator_t<Rng1> constraint?