【问题标题】:How to combine arbitrary predicate functors如何组合任意谓词函子
【发布时间】:2012-11-25 11:58:24
【问题描述】:

我在使用可变参数模板时遇到了以下问题。

假设所有谓词函子的形式为,

class Pred1 {
public:
    Pred1( Args... ); // The signature Args... can vary class to class.

    template <typename T>
    bool operator()(T t);
};

鉴于这些函子,我想创建一个可变参数模板类,如果每个谓词的所有 operator() 都返回 true,则返回 true,即,

template <typename... Preds>
class CombinePredAnd {
public:
    template <typename T>
    bool operator()(T t){
         // returns true if all of the Preds( Args... ).operator()(t) returns true;  
         // Args... should be passed when CombinePredAnd is constructed.
    }
};

对我来说,我不知道将参数传递给 Preds 的每个构造函数。 你能给我一些提示吗? 另外,如果你有更好的设计和相同的功能,请告诉我。

【问题讨论】:

  • 你从哪里得到Args... 来自CombinePredAnd
  • 其实这是我的问题中最难的一点。是否可以在 CombinePredAnd 中创建这样的构造函数?在我看来,这似乎是不可能的。
  • 您想将相同的Args... 传递给每个谓词,还是要单独的Args...(也就是每个谓词一个包)?
  • 他们不一样。起初,我尝试传递 {{args1...}、{args2...}、...} 之类的内容。 argsN... 用于 PredN。
  • 你想有短路行为(在第一次“失败”时放弃)还是不在乎?

标签: c++ c++11 variadic-templates


【解决方案1】:

可能是这样的:

#include <tuple>
#include <type_traits>

template <std::size_t N, std::size_t I, typename Tuple>
struct evaluate_all
{
    template <typename T>
    static bool eval(T const & t, Tuple const & preds)
    {
        return std::get<I>(preds)(t)
            && evaluate_all<N, I + 1, Tuple>::eval(t, preds);
    }
};

template <std::size_t N, typename Tuple>
struct evaluate_all<N, N, Tuple>
{
    template <typename T>
    static bool eval(T const &, Tuple const &)
    {
        return true;
    }
};

template <typename ...Preds>
struct conjunction
{
private:
    typedef std::tuple<Preds...> tuple_type;
    tuple_type preds;

public:
    conjunction(Preds const &... p) : preds(p...) { }

    template <typename T>
    bool operator()(T const & t) const
    {
        return evaluate_all<sizeof...(Preds), 0, tuple_type>::eval(t, preds);
    }
};

template <typename ...Preds>
conjunction<typename std::decay<Preds>::type...> make_conjunction(Preds &&... preds)
{
    return conjunction<typename std::decay<Preds>::type...>(std::forward<Preds>(preds)...);
}

用法:

auto c = make_conjunction(MyPred(), YourPred(arg1, arg2, arg3));

if (c(10)) { /* ... */ }

示例:

#include <iostream>

typedef int T;

struct Pred1
{
    int a;
    Pred1(int n) : a(n) { }
    bool operator()(int n) const { return n >= a; }
};
struct Pred2
{
    int a;
    Pred2(int n) : a(n) { }
    bool operator()(int n) const { return n <= a; }
};

int main()
{
    auto c = make_conjunction(Pred1(1), Pred2(3));

    std::cout << "1: " << c(1) << "\n"
              << "5: " << c(4) << "\n";
}

注意:您也可以将这种方法的“合取”部分设为参数化,因此只需插入std::logical_andstd::logical_or 即可拥有合取和析取。

【讨论】:

    【解决方案2】:

    这里是 Kerrek SB 的替代解决方案:

    #include <tuple>
    #include <type_traits>
    #include <cstdlib>
    
    template <typename HeadPred, typename ...TailPreds>
    struct CombinePredAnd 
    {
        template<typename H, typename ...Ts>
        explicit CombinePredAnd(H const & h, Ts const &...ts)
        : _preds(h, ts...){}
    
        template <typename T>
        bool operator()(T t){
            return eval(t,_preds);
        }
    
    private:
    
        template<typename T, size_t I = 0, typename ...Ps>
        typename std::enable_if<sizeof ...(Ps) == I,bool>::type
        static eval(T t, std::tuple<Ps...>) {
            return true;
        }
    
        template<typename T, size_t I = 0, typename ...Ps>
        typename std::enable_if<sizeof ...(Ps) != I,bool>::type
        static eval(T t, std::tuple<Ps...> const & preds) {
            auto const & pred = std::get<I>(preds);
            return pred(t) && eval<T,I + 1>(t,preds);
        }
    
        std::tuple<HeadPred, TailPreds...> _preds;
    };
    

    这可以按照 Kerrek SB 建议的通用方式概括如下 任意谓词函子的合取或析取,即 通过选择合取或析取来参数化。一个测试程序 附加,使用 gcc 4.7.2 和 clang 3.2 构建:

    #include <tuple>
    #include <type_traits>
    #include <functional>
    #include <cstdlib>
    
    template <class AndOrOr, typename HeadPred, typename ...TailPreds>
    struct dis_or_con_join 
    {
        static_assert(
            std::is_same<AndOrOr,std::logical_and<bool>>::value ||
            std::is_same<AndOrOr,std::logical_or<bool>>::value,     
            "AndOrOr must be std::logical_and<bool> or std::logical_or<bool>");
    
        template<typename H, typename ...Ts>
        explicit dis_or_con_join(H const & h, Ts const &...ts)
        : _preds(h, ts...){}
    
        template <typename T>
        bool operator()(T t){
            return eval(t,_preds);
        }
    
    private:
    
        static const bool conjunction = 
            std::is_same<AndOrOr,std::logical_and<bool>>::value;
    
        template<typename T, size_t I = 0, typename ...Ps>
        typename std::enable_if<sizeof ...(Ps) == I,bool>::type
        static eval(T t, std::tuple<Ps...>) {
            return conjunction;
        }
    
        template<typename T, size_t I = 0, typename ...Ps>
        typename std::enable_if<sizeof ...(Ps) != I,bool>::type
        static eval(T t, std::tuple<Ps...> const & preds) {
            auto lamb = conjunction ? 
                [](bool b){ return b; } :
                [](bool b){ return !b; };               
            auto const & pred = std::get<I>(preds);
            return lamb(lamb(pred(t)) && lamb(eval<T,I + 1>(t,preds)));
        }
    
        std::tuple<HeadPred, TailPreds...> _preds;
    };
    
    template<typename HeadPred, typename ...TailPreds>
    using conjunction  = 
    dis_or_con_join<std::logical_and<bool>,HeadPred,TailPreds...>;
    
    template<typename HeadPred, typename ...TailPreds>
    using disjunction =
    dis_or_con_join<std::logical_or<bool>,HeadPred,TailPreds...>;
    
    
    // Test...
    
    #include <iostream>
    #include <list>
    #include <algorithm>
    
    using namespace std;
    
    // For various predicates with various constructors...
    
    template<typename T>
    struct is_in_list
    // Stores an arbitrary sized list of its type T constructor arguments
    // and then with tell us whether any given T is in its list 
    {
        is_in_list(initializer_list<T> il)
        : _vals(il.begin(),il.end()){}
        bool operator()(T t) const {
            return find(_vals.begin(),_vals.end(),t) != _vals.end();
        }
        list<T> _vals;
    };
    
    int main()
    {
        is_in_list<char> inl03 = {'\0','\3'};
        is_in_list<long> inl013 = {0,1,3};
        is_in_list<float> inl0123 = {0.0f,1.0f,2.0f,3.0f};
    
        conjunction<is_in_list<char>,is_in_list<long>,is_in_list<float>> 
        conj{inl03,inl013,inl0123};
        disjunction<is_in_list<char>,is_in_list<long>,is_in_list<float>> 
        disj{inl03,inl013,inl0123};
    
        cout << "conjunction..." << endl;
        cout << 1 << " is " << (conj(1) ? "" : "not ") 
            << "in all the lists" << endl;
        cout << 0 << " is " << (conj(0) ? "" : "not ") 
            << "in all the lists" << endl;
        cout << 3 << " is " << (conj(3) ? "" : "not ") 
            << "in all the lists" << endl;
        cout << "disjunction..." << endl;       
        cout << 1 << " is in " << (disj(1) ? "at least one " : "none ") 
            << "of the lists" << endl;
        cout << 2 << " is in " << (disj(2) ? "at least one " : "none ") 
            << "of the lists" << endl;
        cout << 3 << " is in " << (disj(3) ? "at least one " : "none ") 
            << "of the lists" << endl;
        cout << 4 << " is in " << (disj(4) ? "at least one " : "none ") 
            << "of the lists" << endl;
        return 0;
    }
    

    可能不清楚的线:

    return lamb(lamb(pred(t)) && lamb(eval<T,I + 1>(t,preds)));
    

    只是利用等价性:

    P or Q = not(not(P) and not(Q))
    

    输出:-

    conjunction...
    1 is not in all the lists
    0 is in all the lists
    3 is in all the lists
    disjunction...
    1 is in at least one of the lists
    2 is in at least one of the lists
    3 is in at least one of the lists
    4 is in none of the lists
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-07
      • 2021-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-16
      相关资源
      最近更新 更多