2017-02-21 17 views
8

私は引数としてファンクタをとり、何らかの処理の後でそれを実行するテンプレートコードを書いています。他の誰かがラムダ、関数ポインタ、さらにはstd::functionという関数を渡すかもしれませんが、それは主にラムダを意味します(他のフォーマットを禁止しているわけではありません)。私はそのラムダをどうすればいいのですか?参考として?または、他の何か。引数としてラムダを渡す - 参照または値による?

コード例 - 上記のコードで

#include <iostream> 
#include <functional> 
using namespace std; 

template<typename Functor> 
void f(Functor functor) 
{ 
    functor(); 
} 

void g() 
{ 
    cout << "Calling from Function\n"; 
} 

int main() 
{ 
    int n = 5; 

    f([](){cout << "Calling from Temp Lambda\n";}); 

    f([&](){cout << "Calling from Capturing Temp Lambda\n"; ++n;}); 

    auto l = [](){cout << "Calling from stored Lambda\n";}; 
    f(l); 

    std::function<void()> funcSTD = []() { cout << "Calling from std::Function\n"; }; 
    f(funcSTD); 

    f(g); 
} 

、私はそれこれらのいずれかを作るの選択をしました -

template<typename Functor> 
    void f(Functor functor) 

template<typename Functor> 
    void f(Functor &functor) 

template<typename Functor> 
    void f(Functor &&functor) 

良い方法と理由は何だろうか?これらに何か制限がありますか?

+0

IMHO、値渡し。 – NathanOliver

+1

私はいつもfunctorテンプレートオブジェクトが値渡しされているのを見てきましたが、私はその理由を知らない。良い質問。 – YSC

+2

related/dupe:http://stackoverflow.com/questions/8196345/passing-functor-object-by-value-vs-by-reference-c – NathanOliver

答えて

4

ラムダがコピーできない場合、コピーを渡すことができないことに注意してください。あなたがそれで逃げることができれば、コピーを渡すことは大丈夫です。一例として
:上記のスニペットで

#include<memory> 
#include<utility> 

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

template<typename F> 
void h(F f) { 
    f(); 
} 

int main() { 
    auto lambda = [foo=std::make_unique<int>()](){}; 

    g(lambda); 
    //h(lambda); 
} 

lambdafooためのコピー可能ではありません。コピーコンストラクタは、std::unique_ptrのコピーコンストラクタが削除された結果として削除されます。
もう一方では、F &&fは転送参照であるlvalueとrvalueの参照とconst参照の両方を受け入れます。
引数に同じラムダを複数回再利用したい場合は、関数がコピーでオブジェクトを取得し、そのオブジェクトをコピーできないように移動する必要があります(実際には、参照によって外側のものをキャプチャするラムダにラップする問題)。

+0

しかし、 'h(std :: move(lambda));'は作業。 – NathanOliver

+2

@NathanOliverもちろん、ラムダをもう一度再利用することはできません。例のように2つの関数に渡したい場合、それは不可能です。 – skypjack

+0

@skypjack followup ques - 2つの同様のオーバーロードを定義する以外に、このようなシナリオで可動ファンクタにフォールバックすることは可能ですか? –

3

ラムダ式はクラスなどの独自のフィールドを持つことができるため、参照をコピー/使用すると異なる結果が生じる可能性があります。ここでは簡単な例です:

template<class func_t> 
size_t call_copy(func_t func) { 
    return func(1); 
} 

template<class func_t> 
size_t call_ref(func_t& func) { 
    return func(1); 
} 

int main() { 
    auto lambda = [a = size_t{0u}] (int val) mutable { 
     return (a += val); 
    }; 
    lambda(5); 

    // call_ref(lambda); – uncomment to change result from 5 to 6 
    call_copy(lambda); 

    std::cout << lambda(0) << std::endl; 

    return 0; 
} 

ところであなたは、性能差は実際にはありませんで、それを判断する場合、ラムダは非常に小さく、コピーが簡単です。

また、lambdaをパラメータとして渡す場合(それを含む変数ではありません)、転送参照を使用して動作するようにしてください。

関連する問題