2015-06-24 7 views
14

Rozumiem funkcję lambda i jej cel w C++ 11. Ale nie rozumiem różnicy między "Przechwytywaniem wartości" i "Podawaniem argumentu". np ..Różnica między przechwytywaniem a przekazywaniem argumentów w funkcjach lambda

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

int add(int a,int b){ 
    return a+b; 
} 

int main(int argc, char** argv){ 

    function <int(int,int)> cppstyle; 
    cppstyle = add; 

    auto l = [] (function <int(int,int)> f,int a, int b) {return f(a,b);}; 

    cout << l(cppstyle,10,30) <<"\n"; 
} 

Wyjście powyższego kodu jest taka sama jak poniżej kod ..

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

int add(int a,int b){ 
    return a+b; 
} 

int main(int argc, char** argv){ 

    function <int(int,int)> cppstyle; 
    cppstyle = add; 

    auto l = [cppstyle] (int a, int b) {return cppstyle(a,b);}; 

    cout << l(10,30) <<"\n";  
} 

jest przechwytywanie wartość jest podobna jak przepuszczenie wartość jako argument? lub przechwytywanie ma jakieś specjalne znaczenie?

+2

Przechwyt jest najbardziej podobny do członka klasy niż argument. – Jarod42

+3

Jedna różnica polega na tym, że przechwytywanie następuje, gdy lambda jest tworzona, podczas gdy przekazywanie wartości następuje po wywołaniu lambda. Jestem całkiem nowy w C++ 11, więc zajmie mi to trochę czasu, aby wymyślić przykład ilustrujący rzeczywiste różnice. –

+2

Przez przechwycenie czegoś skutecznie wiążesz dane z lambdą. Dzięki temu można łatwo ominąć kombinację danych + lambda w dowolnym miejscu. – Kyle

Odpowiedz

10

Różnica między przechwyconym argumentem a argumentem przekazującym można zauważyć z analogią. Rozważmy następujący obiekt funkcyjny:

struct Capture { 
    int &i; 
    int const j; 
public: 
    Capture(int &_i, int &_j) : i(_i), j(_j) {} 
    int operator()(int const a, int const b) { 
    i *= j; 
    return a * b; 
    } 
}; 

funkcja klasy obiektów Capture istnieją dwie zmienne składowe i i j. Istnieje również przeciążenie operator(), które pobiera dwa argumenty wejściowe. Teraz rozważmy następujący lambda:

int i, j; 
[&i, j](int const a, int const b) { 
    i *= j; 
    return a * b; 
}; 

Te zmienne składowe klasy Capture się analogicznie z wychwytywania lambda (tj [&i, j]), natomiast argumentów wejściowych przeciążony operator()a i b są analogiczne z argumentów wejściowych a i b z lambda pokazanego powyżej.

Oznacza to, że jeśli rozpatrujemy obiekt lambda jako obiekt funkcji, jego przechwytywanie jest stanem obiektu funkcji (tj. Jego zmiennych składowych), podczas gdy jego argumenty wejściowe byłyby argumentami wejściowymi przeciążonego operator().

+1

Jedna rzecz do zapamiętania: jeśli przechwycisz referencję, a zmienna, której dotyczy odwołanie, wykracza poza zakres, możesz spowodować awarie. – Kupiakos

+0

@Kupiakos, który jest poprawny, i to samo dotyczy obiektu funkcji 'Capture' w moim przykładzie. Jeśli 'i' wykracza poza zakres, podczas gdy obiekt funkcji wciąż żyje, jest to niezdefiniowane zachowanie, więc niebo jest granicą. – 101010

7

Na wyższym poziomie przechwytujesz dane, które znasz teraz, i przekazujesz dane, których nie masz, dopóki nie musisz wykonać połączenia.

Na przykład, powiedzmy, że chcesz dodać stałą do każdej liczby w wektorze. Twój mógł napisać jak (uwaga: niesprawdzony):

void Add(std::vector<int>& v, int i) 
{ 
    std::for_each(std::begin(v), std::end(v), [i](int& j){ j += i; }); 
} 
+0

Twój kod działa tak samo, jeśli zamiast przechwytywania przekazałeś 'i' jako drugi argument? – J3STER