I have written a simple predicate which I want to pass to std::ranges::views::filter
:
struct even_fn {
constexpr
bool operator()(std::integral auto&& e) const noexcept {
return e % 2 == 0;
}
};
inline constexpr even_fn even;
Sample usage:
using namespace std::ranges;
views::iota(1, 10) | views::filter(even);
This fails with a wall of errors with, I presume, the most meaningful one being:
note: the expression 'is_invocable_v<_Fn, _Args ...> [with _Fn = even_fn&; _Args = {std::__detail::__cond_value_type<int>::value_type&}]' evaluated to 'false'
However, if I remove the std::integral
part from my operator (leaving just auto&&
), the code compiles successfully. Why is that? What's so special about this __cond_value_type
that breaks when we have constrained functors?
This is because std::integral
won't allow reference types to be deduce.
In effect, the filter view will filter with something kind of like this:
filter_function(*it);
This, with most range type will return a lvalue reference to the element.
Since it send a lvalue reference, the only way to make the call to your function is to deduce int&
, so the reference collapsing work that way: int& && --> int&
. Without that calling your function with lvalue is impossible.
But int&
is not an integral type, hence your error!
How can you make your filter work then?
Simply drop the forwaring reference and use a const lvalue reference:
struct even_fn {
constexpr
bool operator()(std::integral auto const& e) const noexcept {
return e % 2 == 0;
}
};
That way, when deducing from a int lvalue, simply int
will be deduced to make the call work.
With concept template parameter, it would be possible to with a higher order concept to make this case work:
template<typename T, concept C>
concept forwarded = C<std::remove_cvref_t<T>>;
And use it like this:
struct even_fn {
constexpr
bool operator()(forwarded<std::integral> auto&& e) const noexcept {
return e % 2 == 0;
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With