2016-04-26 11 views
3

これは概念的な質問かもしれません。私はパラメータとしてlambdaを取る機能を実装しています。しかし、私はlambdaの正確なタイプを理解できませんでした。たとえば:ラムダ閉鎖で移動を適用

auto T = []() { printf("hello world\n"); }; 
auto F = move(T); 
T(); // print "hello world" 
F(); // print "hello world" 

私はTmoveを呼び出した後に考え、Tの内容が消失しました。言い換えれば、私は次のような動作を期待:戻る元の質問に

function<void> T = []() { printf("hello world\n");}; 
auto F = move(F); 
F(); // print "hello world" 
T(); // throw error 

を、function<void()>のクラスメンバーにlambdaを割り当てる/受け渡しのベストプラクティスは何ですか?私は、多くの異なった答えを見て、いくつかはconst function<void()>&を使用して、テンプレートF&&

struct Foo { 

    function<void()> f; 

    // Option 1: 
    void set_f(const function<void()>& in) {f=in;} 

    // Option 2: template 
    template <typename F> 
    void set_f(F&& in) { // what to write here??? } 
} 

を示唆して他の人は、ほとんどの入力タイプをキャプチャするのに十分な一般的なこれら二つのオプションはありますか?

答えて

6

コンパイラがラムダ式で何をするのかについての基本的な誤解があるようです。ラムダ式は一意の名前を持つファンクタに変換されます。ラムダを呼び出すと、ファンクタのoperator()と呼ばれます。

だからあなたの最初の例では、ラムダは、このラムダは、任意の状態を保存していた場合、それからmove INGの状態が移動するこの

struct __uniquely_named_lambda 
{ 
    void operator()() const 
    { 
     printf("hello world\n"); 
    } 
}; 

のようなものを作成しますが、あなたのラムダはステートレスであるため、moveはありません何もない。 operator()の本文を取り除いて別の場所に移動することはできません。

たとえば、これらのステートメントは、指定された署名と一致する任意の呼び出し可能を受け入れることができ、コンテナを出力4 0 4

std::string s{"Test"}; 
auto T = [s]() { std::cout << s.size() << ' '; }; // make a copy of s 
T(); 
auto F = std::move(T); 
T(); 
F(); 

Live demo


std::functionされる生成されます、そして、あなたのラムダですそのような呼び出し可能な1つ。 movestd::functionとすると、保存先の呼び出し可能なターゲットが移動先に移動します。元のターゲットを呼び出しようとするとbad_function_callがスローされますが、これはラムダのmoveとはまったく異なります。私はあなたの例では

template <typename F> 
void set_f(F&& in) 
{ 
    f = std::forward<F>(in); 
} 

Fとしてあなたset_fメンバ関数を記述します


は、左辺値か、呼び出し側から渡された右辺値のいずれかを受け入れることができるようになりますつまり、転送参照です。割り当ては割り当てをコピーするか、または引数を割り当てます。

+0

'f = std :: move(in);'もうまくいくでしょうか? –

+1

@dau_sama 'f'が引数に代入される限りは動作しますが、呼び出し側が期待することはできません。 'set_f'を非' const :: lvalue reference 'で 'std :: function'と呼んでいれば、それはあなたが望んでいないと思われる引数を'動かす'でしょう。いつも 'move'をしたいのであれば、代わりに' F'を値で取るべきです。 – Praetorian