2012-05-12 4 views
3

C++ 11の "LINQ to Objects"ライブラリで作業しています。 私はこのようななめらかにしたいと思っ:私はダウンarr.where_i(...)を書きたいファンクタのparam count型によるC++のオーバーロード

// filtering elements by their value 
arr.where([](double d){ return d < 0; }) 

// filtering elements by their value and position 
arr.where([](double d, int i){ return i%2==0; }) 

- それは醜いです。だから私は、ラムダ型によって関数/メソッドのオーバーロードを必要とする ...

これは私のソリューションです:それはSFINAEソリューション

template<typename F> 
auto my_magic_func(F f) -> decltype(f(1)) 
{ 
    return f(1); 
} 

template<typename F> 
auto my_magic_func(F f, void * fake = NULL) -> decltype(f(2,3)) 
{ 
    return f(2,3); 
} 

int main() 
{ 
    auto x1 = my_magic_func([](int a){ return a+100; }); 
    auto x2 = my_magic_func([](int a, int b){ return a*b; }); 
    // x1 == 1+100 
    // x2 == 2*3 
} 

ですか? 私に何を提案できますか?

+0

このソリューションは機能しますが、私はmy_magic_funcを書くために引数の型を知る必要があります。それは快適ではありません。 – k06a

+0

boost :: rangeを見てみるといいでしょう。 –

+0

私はC#LINQスタイルをコピーすることを主なターゲットとしたライブラリに取り組んでいます...興味があれば、ここで入手できます:http://code.google.com/p/boolinq/ – k06a

答えて

3

多分可変引数何か:

#include <utility> 

template <typename F, typename ...Args> 
decltype(f(std::declval<Args>()...) my_magic_func(F f, Args &&... args) 
{ 
    return f(std::forward<Args>(args)...); 
} 

編集:あなたも同じことをした、戻り値の型のためtypename std::result_of<F(Args...)>::typeを使用することができます。

+1

非常に残念ですが、MSVC++ 2010はバリデーションテンプレートをサポートしていません – k06a

+0

'std :: result_of'は使用できません。それが指定されている方法では、SFINAEが正しく使用されていることが保証されません。また、私の経験では、ハードエラーが発生することもあります。 –

+0

-1これは完全に質問のポイントを逃す。 'my_magic_func'は単項式であり、パラメータパックをとっていません。それは与えられたファンクタのアリティを検出し、それに応じて異なる引数で呼び出すことになっています。 – ildjarn

2

あなたは確かにSFINAEをあなたのソリューションに入れてください。一般的に言えば、結果は次のようになります。

template< 
    typename Functor 
    , typename std::enable_if< 
     special_test<Functor>::value 
     , int 
    >::type = 0 
> 
return_type 
my_magic_func(Functor f); 

template< 
    typename Functor 
    , typename std::enable_if< 
     !special_test<Functor>::value 
     , int 
    >::type = 0 
> 
return_type 
my_magic_func(Functor f); 

ように一つだけの過負荷が一度にアクティブになること - すべての今慎重special_testは、私たちが望む行動を持っていることを作り上げているのまま。テストをあまりにも具体的にしたくないので、これは慎重なバランスのとれた行動です。さもなければ私達は一般性を失う。ジェネリックコードを書くときは非常に残念です。あなたはあまり情報を与えていません(例えば、ラムダのサポートに厳密に関心がありますか?単相性ファンクターか多型ファンクターか)。value_typeエイリアスへのアクセスが今のところあなたの例ではdoubleになります。

このように、指定された型がCallable(標準コンセプト)であることを確認する条件の例を次に示します。bool(value_type);すなわち、それはある種の述語だと:

個人的に
template<typename Functor, typename ValueType> 
struct is_unary_predicate { 
    typedef char (&accepted)[1]; 
    typedef char (&refused)[2]; 

    void consume(bool); 

    template< 
     typename X 
     , typename Y 
     , typename = decltype(consume(std::declval<X>()(std::declval<Y>()))) 
    > 
    accepted 
    test(X&&, Y&&); 

    refused test(...); 

    static constexpr bool value = 
     sizeof test(std::declval<Functor>(), std::declval<ValueType>()) 
     == sizeof(accepted); 
}; 

私だけtemplate<typename Functor, typename ValueType> using is_unary_predicate = is_callable<Functor, bool(ValueType)>;のようなものを書く必要があるだろうように、私はis_callable<F, Signature>形質を持っている(と同様に、私が代わりにmy_magic_funcの2番目のオーバーロードが可能せるのis_binary_predicate別名を持つことができますキャッチオール)。おそらく、SFINAEの将来の使用のために同様の特性を使用したいと思うかもしれません(ただし、バリデーショナルテンプレートなしで書くのはやや苦しいかもしれません)。

+0

「受け入れ」と「拒否」は異なるサイズであるべきですか? – ildjarn

+0

@ildjarnありがとう、修正済み。 –

関連する問題