32

I understand the lambda function and the purpose of it in c++ 11. But i do not understand the difference between "Capturing the value" and "Passing an argument". For Instance..

#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";   
}

The output of the code above is same as the code below..

#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";    
}

Is "capturing a value" similar to "passing a value as an argument"? or capture has some special meaning?

01000001
  • 833
  • 3
  • 8
  • 21
  • 5
    The capture is most similar to class member than argument. – Jarod42 Jun 24 '15 at 22:49
  • 8
    One difference is that the capture occurs when the lambda is created whereas passing a value occurs when the lambda is called. I'm fairly new to C++11, so it will take some time for me to come up with an example that illustrates the real differences. – Code-Apprentice Jun 24 '15 at 22:50
  • 2
    By capturing something you effectively tie data to the lambda. You are then able to pass around the combination of data + lambda easily wherever it needs to go. – Kyle Jun 24 '15 at 22:52
  • @Kyle By 'tie the data to the lambda' you mean i can use captured data later on, am i right? if so can you show me one example please. Thanks – 01000001 Jun 24 '15 at 22:59
  • 1
    I'm not sure but I don't think it's common to do your own lambda lifting, Yes, we have implemented your function `x` but you need to pass constant `z` as the first argument to get a sensible result. It's leaky and ugly. You need closures and that means capturing variables that are free (available) which the user shouldn't have to know of for it to work. – Sylwester Jun 24 '15 at 23:22
  • 1
    @ArpitParasana I'm not familiar enough with C++11's lambda syntax to give a solid example. The idea is that you could do things like pass your lambda with captured data to the function `void fun(std::function lambda){..}` – Kyle Jun 24 '15 at 23:34
  • @ArpitParasana would you mind explaining `function cppstyle;` this syntax? – J3STER Mar 19 '17 at 01:54
  • 1
    @J3STER This link has a great explanation: http://en.cppreference.com/w/cpp/utility/functional/function; its basically a variable of type function which can have a handle to other function which has a signature as specified in declaration (in this case a function which accepts two integers and returns an integer). – 01000001 Mar 20 '17 at 17:02

3 Answers3

29

The difference between a captured argument and a passing argument could be seen with an analogy. Consider the following function object:

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;
  }
};

In function object class Capture there are two member variables i and j. There's also overloaded operator() which takes two input arguments. Now consider the following lambda:

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

The member variables of class Capture are in analogy with the lambda capture (i.e., [&i, j]), whereas input arguments of overloaded operator() a and b are in analogy with input arguments a and b of the lambda shown above.

That is, if you consider a lambda as a function object, its capture is the state of the function object (i.e., its member variables) whereas its input arguments would be the input arguments of the overloaded operator().

101010
  • 41,839
  • 11
  • 94
  • 168
  • 1
    One thing to note: if you capture by reference and the referenced variable goes out of scope, you can cause crashes. – Alyssa Haroldsen Jun 24 '15 at 23:27
  • 1
    @Kupiakos that is correct, and the same goes for `Capture` function object in my example. If `i` goes out of scope while the function object is still alive then this is undefined behaviour so sky is the limit. – 101010 Jun 24 '15 at 23:28
  • 1
    @AlyssaHaroldsen "you can cause crashes" -> "you have undefined behaviour" – Caleth May 30 '19 at 15:56
22

At a higher level, you capture the data you know now, and you pass in the data you don't have until you need to make the call.

For instance, let's say you wanted to add a constant to every number in a vector. Your could write it like (caution: untested):

void Add(std::vector<int>& v, int i)
{
    std::for_each(std::begin(v), std::end(v), [i](int& j){ j += i; });
}
Nevin
  • 4,595
  • 18
  • 24
  • 1
    would your code work the same, if you passed `i` as a second argument instead of capturing it? – J3STER Mar 19 '17 at 02:15
  • @J3STER no, it simply *won't compile*. `std::for_each` wants a function of *one* parameter – Caleth May 30 '19 at 15:57
6

The capture of i value is set when the lambda was defined, while when i is passed as argument (j), it is changing in the loop.

#include <iostream>
using namespace std;

int main(int argc,char **argv)   {
    auto i=5;
    auto f = [=](int j) {cout<<"capture i="<<i<<", passing i as j="<<j<< endl; };
    while (i<30) {
        i += 10;
        f(i);
    }
}

--- This will be the output:

lambda capture i=5, passing i as argument j=15

lambda capture i=5, passing i as argument j=25

lambda capture i=5, passing i as argument j=35

vik_78
  • 1,107
  • 2
  • 13
  • 20
Raul1962
  • 61
  • 1
  • 1
  • 2
    Thanks for exposing the pitfall. Along the lines of the lambdas as function object analogy, it means that the captured `i = 5` is the state of the function object during the creation and when the `i` changes inside the loop, the state of the function object is not altered. – talekeDskobeDa Aug 31 '20 at 13:24
  • Simple and concise. This answer should be rated better. – Francisco Rodríguez Jul 08 '22 at 02:38
  • Mind you, that this is only true for capture by copy `[=]`. If you capture by reference `[&]` the value of `i` will be the same as `j` – Seriously Jan 20 '23 at 17:59