1

次の問題で可変個引数テンプレートを使用するのに苦労しています。

すべての述語関手が次の形式であると仮定します。

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の各コンストラクターに引数を渡すことはわかりません。ヒントを教えてください。また、同じ機能でより良いデザインがあれば教えてください。

4

2 に答える 2

5

多分このように:

#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_andまたはを接続するだけで接続詞と論理和を設定できますstd::logical_or

于 2012-11-25T12:22:00.913 に答える
0

KerrekSBの代替ソリューションは次のとおりです。

#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が、接続詞または論理和の選択によってパラメーター化される任意の述語関数の一般的な接続詞または論理和に提案する方法で、次のように一般化できます。テストプログラムが追加され、gcc4.7.2とclang3.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
于 2013-04-08T10:11:17.990 に答える