2016-11-11 10 views
1

Passing different lambdas to function template in c++と似た問題がありましたが、ラムダの代わりにstd::bindで作成されたラッパーを使用しています。std :: bindの結果をstd :: functionに渡す "overloads"

私はstd::functionの異なる形態をとる方法Addの2つのオーバーロードがあります。

template<typename T> 
struct Value 
{ 
    T value; 
}; 

template <typename T> 
void Add(Value<T> &value, function<bool()> predicate) 
{ 
} 

template <typename T> 
void Add(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate) 
{ 
} 

これは今ラムダと正常に動作しますが、std::bindで結合ファンクタで失敗します。

struct Predicates 
{ 
    bool Predicate0() { return true; } 
    bool Predicate1(const Value<int> &) { return true; } 
}; 

Predicates p; 
Add(i, std::bind(&Predicates::Predicate0, &p)); 

で失敗

エラーC2668: '追加':オーバーオールへのあいまいな呼び出しDED機能

Add(i, std::bind(&Predicates::Predicate1, &p, _1)); 

は静的アサート(のVisual C++ 2015、アップデート3)で失敗します。境界

のうち

タプルインデックスが方法はありますラムダとバインドされたファンクタの両方で動作させるには? SFINAEを使用してis_bindable_expressionに基づいて個々のオーバーロードを有効にし、引数の型をチェックすると思いますが、私はそれをまとめません。

+1

'std :: bind(&Predicates :: Predicate0、&p)()'と 'std :: bind(&Predicates :: Predicate0、&p)(値 {})'は有効です。 (後者の場合、引数 'Value {}'は無視されます)。これは 'std :: bind'の機能です。 – cpplearner

+1

あなたは過負荷を悪用しています。 "この引数でインスタンスを呼び出すことができる"または "他の引数で呼び出すことができるインスタンス"のプロパティを満たす型の共通部分は空ではありません。 – milleniumbug

答えて

2

停止として定義されているタグで

template <typename T> 
void AddImpl(Value<T> &value, function<bool()> predicate, tag::default_) 
{ 
    predicate(); 
} 

template <typename T> 
void AddImpl(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate, tag::default_) 
{ 
    predicate(value); 
} 

template <typename T, typename U> 
void AddImpl(Value<T>& value, U&& bind_expression, tag::bind) 
{ 
    bind_expression(value); 
} 

template<typename T, typename U> 
void Add(T&& t, U&& u) 
{ 
    AddImpl(std::forward<T>(t), std::forward<U>(u), tag::get_tag<std::decay_t<U>>{}); 
} 

:私の意見では、適切に名付けられたタグを上の派遣を読みやすくするためのより良いだろう。これは、ランダムな機能と癖の混乱です。

今日変わったのは、std::bindは、無制限の引数を受け入れ、余分なものをすべて破棄するということです。明日はstd::bindの結果をstd::bindに渡すと奇妙な魔法が起きるという事実に遭遇するかもしれません。

std::bindは、言語に追加されたときに同時にboostに移植されました。 Lambdasはほとんどすべての問題を解決します。bindは明確な構文で行います。bindは、特にのlambdasが利用可能なときには、C++ 14の投稿をしません。 (ほとんどのC++ 11コンパイラもautoラムダをサポートしました)。

両方を適用したときにどちらか一方が優先オーバーロードになるように関数を記述できます。しかし、そうすることで、あなたのインターフェイスにノイズが増えます。この場合、理由が欲しいのは、std::bindが何かばかげているからです。

stdライブラリの設計が間違っているエンジニアは、その価値はありません。 stdライブラリの不十分に設計されたビットを使用するか、明示的に使用する時点で使用を停止するだけです。それに失敗


、次の操作を行います。私たちはいくつかの厄介なSFINAE検出を投げるあなたが使用したい2つのオーバーロードのどちらに、と明示的に好む

template <class T, class F, 
    std::enable_if_t< 
    std::is_convertible< 
     std::result_of_t<std::decay_t<F> const&(Value<T> const&)>, 
     bool 
    >{}, int 
    > = 0 
> 
void Add(Value<T> &value, F&& f) 
{ 
    // do pass f Value<T> 
} 
template <class T, class F, 
    std::enable_if_t< 
    !std::is_convertible< 
     std::result_of_t<std::decay_t<F> const&(Value<T> const&)>, 
     bool 
    >{} 
    && std::is_convertible< 
     std::result_of_t<std::decay_t<F> const&()>, 
     bool 
    >{}, int 
    > = 0 
> 
void Add(Value<T> &value, F&& f) 
{ 
    // do not pass f Value<T> 
} 

を。

これは価値がありません。

+0

+1私たちはC++ 14ラムダを持っているので、特に 'std :: bind'の使用を止めさせるために+1し、' std :: bind'は実際にはboostからのその貧しい兄弟です。私はlambdaよりもバインドを好む理由を考えようとしましたが、私が思いついた半良品は、イベントハンドラシーケンスを作成するための静的リターン型ではありませんでしたが、あまり意味がありませんあなたがそのタイプを綴ることができなければ。 – krzaq

+1

@krzaq場合によっては、インラインヘッダファイルにラムダを返す関数でODRの問題があります。私は、C++の標準委員会がUBを(ほとんど常に完全に無害な)トリガーしない場所で扱うことを願っています。 – Yakk

+0

@ Yakk 'std :: bind'の問題について私は知らなかった、私は将来それを避けようとします。それにもかかわらず、あなたの厄介なSFINAE検出を試みましたが、とにかく動作しないようです。 VC++ではSTL内の静的なアサーション 'tuple index out of bounds'とコンパイル時にGCCでコンパイルするのに問題がありますが、その結果は私が期待するものではありません。バインドされたメソッドが異なる「Add」を呼び出すと、同じオーバーロードが呼び出されるようです。 http://melpon.org/wandbox/permlink/pcwxYAQcaoSxFQNi – manison

3

私はあなたが望むことはできないと思います。

is_bind_expressionを使用して、引数がstd::bindへの呼び出しによって生成された型であるかどうかを確認できますが、呼び出し可能な引数の数を指定する方法はありません。コメントで述べたcpplearnedように、これはstd::bindの機能です:

g()への呼び出しで供給されている引数の一部がgに格納されている任意のプレースホルダにマッチし ない場合は、未使用の引数は を評価され廃棄される。

つまり、両方のオーバーロードが同じように有効です。あなたはすべてのbind結果を得るために同じ過負荷を共有する気にしない場合は


、あなたはすべてのパラメータを渡すことができますし、それらを自由に破棄されてみましょう:

template <typename T> 
void AddImpl(Value<T> &value, function<bool()> predicate, std::false_type) 
{ 
    predicate(); 
} 

template <typename T> 
void AddImpl(Value<T> &value, block_deduction<function<bool(const Value<T> &)>> predicate, std::false_type) 
{ 
    predicate(value); 
} 

template <typename T, typename U> 
void AddImpl(Value<T>& value, U&& bind_expression, std::true_type) 
{ 
    bind_expression(value); 
} 

template<typename T, typename U> 
void Add(T&& t, U&& u) 
{ 
    AddImpl(std::forward<T>(t), std::forward<U>(u), std::is_bind_expression<std::decay_t<U>>{}); 
} 

demo

しかし、このbooleanパラメータを使用するのと同様です。

std::bindを使用して
namespace tag 
{ 
struct default_{}; 
struct bind{}; 

template<typename T, typename = void> 
struct get_tag : default_ {}; 

template<typename T> 
struct get_tag<T, std::enable_if_t<std::is_bind_expression<T>::value>> : bind {}; 

} 

demo

+0

そして、私はこの機能を利用することができ、引数を取る 'function'を持つ唯一の方法' Add'あります \tをテンプレート \tボイド(バリュー&値、block_deduction <機能<ブール(constの値&)を追加します。 >>述語); そして常に引数を使って '述語'を呼び出してください。バインドされたファンクタのプレースホルダと一致しない場合、引数は無視されます。しかし、それはラムダで壊れます。これは良いC++ソリューションですか?何をお勧めしますか? – manison

+0

私はあなたのユースケースを知りませんが、それは正しいかもしれませんが、それはまた大胆であるかもしれません。あなたが議論を破棄しても大丈夫なら、私は家に帰るときにこれを行う例に従います(2-3時間) – krzaq

+0

@manisonは答えをチェックします。これが役に立つと願っています。 – krzaq

関連する問題