为什么使用整数类型约束 auto 使其不可用于 views::filter?

Fur*_*ish 6 c++ c++-concepts c++20

我写了一个简单的谓词,我想传递给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;
Run Code Online (Sandbox Code Playgroud)

示例用法:

using namespace std::ranges;
views::iota(1, 10) | views::filter(even);
Run Code Online (Sandbox Code Playgroud)

这失败了一大堆错误,我认为最有意义的错误是:

note: the expression 'is_invocable_v<_Fn, _Args ...> [with _Fn = even_fn&; _Args = {std::__detail::__cond_value_type<int>::value_type&}]' evaluated to 'false'
Run Code Online (Sandbox Code Playgroud)

但是,如果我std::integral从我的操作员中删除该部分(只留下auto&&),代码将成功编译。这是为什么?__cond_value_type当我们有约束函子时,它有什么特别之处呢?

Gui*_*cot 6

这是因为std::integral不允许推断引用类型。

实际上,过滤器视图将使用以下内容进行过滤:

filter_function(*it);
Run Code Online (Sandbox Code Playgroud)

这对于大多数范围类型将返回对元素的左值引用。

由于它发送一个左值引用,因此调用您的函数的唯一方法是推断int&,因此引用折叠以这种方式工作:int& && --> int&。没有那个用左值调用你的函数是不可能的。

int&不是整数类型,因此您的错误!

那你怎么能让你的过滤器工作呢?

只需删除转发引用并使用 const 左值引用:

struct even_fn {
    constexpr
    bool operator()(std::integral auto const& e) const noexcept {
        return e % 2 == 0;
    }
};
Run Code Online (Sandbox Code Playgroud)

这样,当从 int 左值推导时,只会int推导使调用工作。


使用概念模板参数,可以使用更高阶的概念来使这种情况起作用:

template<typename T, concept C>
concept forwarded = C<std::remove_cvref_t<T>>;
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

struct even_fn {
    constexpr
    bool operator()(forwarded<std::integral> auto&& e) const noexcept {
        return e % 2 == 0;
    }
};
Run Code Online (Sandbox Code Playgroud)