1

I wonder why I can use global variables (thanks to Chris Drew for correcting me) in lamdas and why I don't need to capture them:

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

size_t i = 0;
vector<int> v = {1,2,3};

int main()
{
    auto lambda = [](){i = v.size();};
    lambda();
    cout << i << endl;

    return EXIT_SUCCESS;
}

In this minimum working example I am accessing the size_t and the vector without capturing them. I would have to if they were declared inside the main-method. Why is that so and how can I copy the size_t and the vector? I tried to use [=] as capture list but it does not copy v and i.

Bambino
  • 405
  • 4
  • 15

4 Answers4

3

One way to capture a global variable by value is to use C++14 generalized lambda captures:

#include <iostream>
#include <vector>

size_t i = 0;
std::vector<int> v = {1,2,3};

int main() {
    auto lambda = [myi = i, myv = v]()mutable{myi = myv.size();};
    lambda();
    std::cout << i << std::endl;
}

Live demo.

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
2

In your case i and v are global variable, accessible to the whole TU.

As you asked how to capture them by value, I think you should be able to capture them using both [=] or listing the variables [i, v], but this would lead to an error, because they will be read-only and you are assigning to i inside the lambda body.

Option 1:
capture i by ref and v by value (if that makes sense at all...):

#include <iostream>  
#include <vector>  
using namespace std;
int main() {
    size_t i = 0;
    vector<int> v = {1,2,3};
    auto lambda = [&i,v](){i = v.size();};
    lambda();
    cout << i << endl;

    return EXIT_SUCCESS; }

http://ideone.com/fkn4za

Option 2:
use a mutable lambda and capture both by value (this makes even less sense).
I.e. see this question on SO.
Please note that in this case also i will be captured by value, thus the global i will not be assigned, remaining at value == 0.

http://ideone.com/qwlFVv

Community
  • 1
  • 1
roalz
  • 2,699
  • 3
  • 25
  • 42
  • And how can I copy it? Using `[=]` as capture list doesn't work – Bambino Jan 24 '17 at 13:45
  • See my addition to the answer. It's not really clear why you want to capture them (both) by value. – roalz Jan 24 '17 at 13:54
  • I was expecting to see a zero getting printed because I don't operate on the global variables since I am trying to copy them. – Bambino Jan 24 '17 at 14:03
  • As in option 2, if you capture i by value, the global i will not be assigned, remaining at value == 0. – roalz Jan 24 '17 at 14:07
  • That does not seem to work when using global variables. – Bambino Jan 24 '17 at 14:15
  • Right, global variables are accessed directly anyway. – roalz Jan 24 '17 at 14:20
  • Any way to get that working? I mean copying the globals with the capture list. An obvious solution is to copy it with new variables though, but that is not what I intended. – Bambino Jan 24 '17 at 14:22
  • 1
    Ok, I think I've found the reason why this is not possible. Explicitly capturing a global should be an error or UB, because §5.1.2/10 says: "The identifiers in a capture-list are looked up using the usual rules for unqualified name lookup (3.4.1); each such lookup shall find a variable with automatic storage duration declared in the reaching scope of the local lambda expression." – roalz Jan 24 '17 at 14:39
  • 1
    There's also a more detailed answer on SO: http://stackoverflow.com/questions/9199744/gcc-incorrectly-captures-global-variables-by-reference-in-lambda-functions – roalz Jan 24 '17 at 14:40
2

Lambdas have accesses to global variables, and static variables in a class without explicitly capturing them. If it were a local variable, then you program would be ill-formed.

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    size_t i = 0;
    vector<int> v = {1,2,3};
    auto lambda = [](){i = v.size();};   //Error, 
    lambda();
    cout << i << endl;

    return EXIT_SUCCESS;
}

As seen here

Same with static variables in a class:

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

struct S{
    void touch(){ []{ k = 89; }(); }
    static int getK(){ return k; }
private:
    static int k;
};

int S::k = 0;


int main()
{
    S s;
    std::cout << S::getK() << std::endl;
    s.touch();
    std::cout << S::getK() << std::endl;
}

As seen Here

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
  • I know. I just used the wrong term to describe the vector and the size_t. I wonder how I can copy the size_t and the vector? Using `[=]` as capture list doesn't work. – Bambino Jan 24 '17 at 13:47
  • If you capture a local variable and you need to modify the local capture, you need to use a `mutable` lambda – WhiZTiM Jan 24 '17 at 13:55
  • Using mutable seems to work when using local variables. But using it with global variables produces the original output. – Bambino Jan 24 '17 at 14:18
2

Your lambda is basically converted to a functor, it's the same as :

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

size_t i = 0;
vector<int> v = {1,2,3};

struct lambda
{
  void operator()() { i = v.size(); }
};

int main()
{
    lambda x;
    x();
    cout << i << endl;

    return EXIT_SUCCESS;
}

As you can see, lambda can perfectly access any global variable, it's even in their name, the variable is globally accessible.

If i and v were local to main() then we'd have an issue and we'd have to capture them.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • Thanks, the core of my question, although I didn't express that very well, was to ask how I can copy the vector and the size_t. It seems like I should just copy them with a new size_t and vector. – Bambino Jan 24 '17 at 13:52
  • @Bambino Yes, you can copy them inside the lambda and then work on those variables. – Hatted Rooster Jan 24 '17 at 13:53