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::iterator
s
(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?