如何创建用于组合二元谓词的通用函数?

How to create a generic function for combining binary predicates?

有几个相关的问题,例如algorithms with multiple (unary) predicates, or multiple predicates in general. But they do not seem to tackle the generic case that I'm aiming at, and the answers are often out-dated in that they refer to constructs that are deprecated in C++11. It might all boil down to this question related to type deduction in lambdas,但我不确定是否可以将其视为重复。

我试图创建一个组合两个任意二元谓词的函数(在 "types that implement a two-argument operator() and return a bool" 的含义中)。我的目标是有可能编写这样的代码:

auto p0 = somePredicate();
auto p1 = someOtherPredicate();
auto p2 = evenMorePredicates();

auto combined = and(p0, or(p1, p2));

我知道使用 lambda 可以实现类似的功能(正如上面链接的其中一个问题的答案中所建议的那样),但这需要在 lambda 本身中重复参数类型。我想知道这样的 andor 函数如何真正实现 一般 - 特别是对于具有任意参数类型的二元谓词。

我的基本方法(和一个提示性示例)如下:

#include <functional>
#include <iostream>

template <typename A, typename B, typename P0, typename P1>
std::function<bool(const A&, const B&)> and(
    const P0& p0, const P1& p1)
{
    return [p0, p1](const A& a, const B& b)
    {
        return p0(a, b) && p1(a, b);
    };
}

int main(int argc, char* argv[])
{
    auto p0 = [](const int& a, const int& b)
    {
        return a < 0 && b > 0;
    };
    auto p1 = [](const int& a, const int& b)
    {
        return a < 1 && b > 4;
    };

    // This is what I want:
    //auto combined = and(p0, p1);

    // This works, but ... 
    auto combined = and<int, int, std::function<bool(const int&, const int&)>, std::function<bool(const int&, const int&)>>(p0, p1);

    std::cout << "combined(-3,7) : " << combined(-3, 7) << std::endl;
    std::cout << "combined(-3,1) : " << combined(-3, 1) << std::endl;

    return 0;
}

无论如何,问题似乎是无法在调用站点推导参数类型的模板参数。我根据 Whosebug 上的其他相关问题尝试了其中的变体,但无济于事。我的直觉是必须有一个(简单的)解决方案,绕过 lambda 类型推导系统的一些限制,但我显然还没有找到正确的路径。

你可以写一个仿函数 class 来承载你的表达式:

template <typename P0, typename P1>
class AndFunctor {
public:
    AndFunctor (P0 p0, P1 p1)
     : m_p0{std::move(p0)}, m_p1{p1}
    {}

    template <typename T, typename U>
    bool operator() (T&& t, U&& u) {
        return m_p0(t, u) && m_p1(t, u);   
    }

private:
    P0 m_p0;
    P1 m_p1;
};

然后 return 在您的 and 函数中创建一个实例(and 是关键字,因此此代码将其重命名为 and_p

template <typename P0, typename P1>
AndFunctor<P0,P1> and_p(P0 p0, P1 p1)
{
    return { std::move(p0), std::move(p1) };
}

那你就按照你想象的那样使用吧:

auto combined = and_p(p0, p1);

Live Demo