是否可以确定可调用对象是否是谓词(即返回 bool)?

时间:2021-04-05 18:49:12

标签: c++ c++17 template-meta-programming c++20 c++-concepts

试图重写这样的谓词组合器

auto constexpr all = [](auto const&... predicates){
    return [predicates...](auto const&... x){
        return (predicates(x...) && ...);
    };
};

this 的小泛化)以某种方式在输入具有不同参数/参数的非谓词/谓词时会产生有意义的错误,我开始写这样的东西:

template<typename T, typename = void>
struct IsPredicate : public std::false_type {};

template<typename T>
struct IsPredicate<T, std::enable_if_t<std::is_same_v<bool, return_type_of_callable_T>, void>>
  : public std::true_type {};

然后我盯着它看了一会儿......如果我什至不知道如何调用它,我如何检查函数的返回类型是什么?

我看到了:

  • 我什至无法将 decltype(overloaded_predicate_function) 传递给 IsPredicate,因为模板类型推导不能在名称重载时发生,
  • 即使我只讨论函数对象,第一个要点的问题也可能适用于 operator(),以防它过载。

所以我的问题是:甚至可以确定任意可调用对象的返回类型吗?

我最感兴趣的是 C++17 的答案,但是,为什么不呢?我还想知道 C++20 在这方面提供了什么概念。

3 个答案:

答案 0 :(得分:3)

<块引用>

所以我的问题是:甚至可以确定任意可调用对象的返回类型吗?

没有。您只能在非常狭窄的情况下执行此操作:

  • callable 是一个指向成员数据的指针/指向成员函数的指针
  • callable 是一个指向函数的指针/引用
  • 可调用对象是具有单个非重载函数调用运算符的函数对象,该运算符不是模板,并且没有将函数转换为函数指针/引用

就是这样。如果你有一个函数对象,它的调用运算符要么是重载的,要么是模板,你真的无法弄清楚它的返回类型是什么。它的返回类型可能取决于其参数类型,您可能无法知道参数类型是什么。也许这是一个调用运算符模板,它只接受一些您无法了解的特定类型,但它是这些类型的谓词?

你能做的最好的事情就是推迟检查,直到你知道什么是参数。然后 C++20 已经为您提供了这个概念 (predicate):

inline constexpr auto all = []<typename... Ps>(Ps const&... predicates){
    return [=]<typename... Xs>(Xs const&... x)
        requires (std::predicate<Ps const&, Xs const&...> && ...)
    {
        return (std::invoke(predicates, x...) && ...);
    };
};

请注意,您应该使用 std::invoke 来允许将指向成员的指针也作为谓词(这就是 std::predicate 检查的内容)。

答案 1 :(得分:1)

如果不指定参数类型(通常通过提供实际参数来完成),则无法确定可调用对象的返回类型,因为返回类型可能取决于参数类型。 “decltype(potential_predicate)”不起作用,但“decltype(potential_predicate(args...))”是另一回事。

第一个将是可调用本身的类型(函数指针或类类型或其他类型),而第二个将生成可调用表达式的返回类型。

答案 2 :(得分:0)

不能给你一个 C++17 的答案,但因为你也问了概念:

requires 表达式表明 () 运算符已重载并返回 bool。我认为经典意义上的谓词需要两个参数,但这个概念可以很容易地扩展到满足这个要求。

template<typename T>
concept IsPredicate =
    requires(T a) {
        { a() } -> std::same_as<bool>;
    };
相关问题