【问题标题】:Is it possible to determine if a callable is a predicate (i.e. returns bool)?是否可以确定可调用对象是否是谓词(即返回布尔值)?
【发布时间】:2021-07-01 14:52:21
【问题描述】:

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

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 的概念在这方面提供了什么。

【问题讨论】:

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


    【解决方案1】:

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

    没有。您只能在非常有限的情况下这样做:

    • 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 检查的内容)。

    【讨论】:

    • 补充我的问题,我想说,关于那些模棱两可的地方(返回类型可能取决于...),我想我有兴趣确定具体的name 是一个谓词,不管它是如何被调用的,即如果它在它的每一个实例化/重载中返回一个bool。也许这只是一个无意义的要求,因为例如,客户端代码可以专门化具有非bool 返回类型的模板?
    • @Enlico 如果没有实际潜在地实例化模板,您将无法知道这一点,这可能会失败。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      不能给你一个 C++17 的答案,但既然你也问过概念:

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

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

      【讨论】:

      • 这里不需要古典意义。谓词“是肯定的?”是一元的,谓词是“相等?”是(至少)二元的,谓词“这些 3D 向量是共面的吗?”是(至少)三元的。谓词是返回布尔值的任何东西,它表示其参数的条件是否被验证。
      • @flowit 您的concept 定义错误地使用了预标准化语法。 concept 定义的正确 C++20 语法无法指定从表达式返回的确切类型(例如 -&gt; bool),它只能指定它满足的 concept(例如 -&gt; std::same_as&lt;bool&gt;-&gt; std::convertible_to&lt;bool&gt;)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-19
      • 2020-04-10
      • 1970-01-01
      • 1970-01-01
      • 2012-04-11
      • 2012-01-06
      相关资源
      最近更新 更多