mrks
mrks

Reputation: 8333

C++: Change variables captured in lambda from outside

Is there any way to change the variable captured by copy in a lambda from the outside?

Example:

#include <iostream>

int main() {
    int x = 3;
    auto f = [x]() { std::cout << x << std::endl; };
    // is there anything I can do here to make f print 4?
    f();
}

If this is possible, it feels really dirty. Is there a compelling reason why I shouldn't even think about doing so?

Upvotes: 4

Views: 4664

Answers (5)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275350

struct do_nothing{ template<class...Args> void operator()(Args&&...)const{}};
template<class...cmd>
struct maybe_run_t;
template<> struct maybe_run_t<>:do_nothing{};
template<class cmd>struct maybe_run_t<cmd>{
  cmd&& value;
  template<class...Args>
  void operator()(Args&&...args)const{
    value(std::forward<Args>(args)...);
  }
};
template<class...Args>
maybe_run_t<Args...> maybe_run(Args&&...args){
  return {std::forward<Args>(args)...};
}

Then:

int x = 3;
auto f = [x](auto&&...cmd)mutable { maybe_run(cmd...)(x); std::cout << x << std::endl; };
// is there anything I can do here to make f print 4?
f([](auto&x){x=4;});
f();

Prints 4\n4\n.

You have an optional argument. If you pass one, it gets to modify x. If you do not, the code works.

You could also do flow control based on its existence or return value.

Upvotes: 0

skypjack
skypjack

Reputation: 50540

In C++14, you can do this:

#include<iostream>

int main() {
    int x = 0;
    auto l = [x]()mutable->decltype(auto){
        std::cout << x << std::endl;
        return (x);
    };
    l() = 42;
    l();
}

Actually, it's pretty easy to do something similar also in C++11:

auto l = [x]()mutable->decltype(x)&{
    std::cout << x << std::endl;
    return x;
};

That is, you can modify the variable captured by copy simply by returning it by reference.

Upvotes: 0

Chintan
Chintan

Reputation: 372

To answer your question, no it is not possible to change local variables of a function/functor (which is what lambda is) from the outside.

Think of lambda as "regular" functions whose name you don't have. Just like for regular functions you can't change local variable values from the outside (params passed by value), you can't do it with lambda too. This is a necessary requirement for security & predictability of the result.

If you have a problem where you need to do this, as others have suggested use [&] syntax.

(There exists https://www.eecs.umich.edu/courses/eecs588.w14/static/stack_smashing.pdf but I think that relies on knowledge of compiler/system implementation and usually gets into undefined behaviour)

Upvotes: 1

IanM_Matrix1
IanM_Matrix1

Reputation: 1594

Revert back to the original 'lambda' type: create a struct or class with an operator()

struct myLambda
{
    int x;
    myLambda(int x) : x(x) {};
    operator() { std::cout << x << '\n'; }
};

Upvotes: 0

Oodini
Oodini

Reputation: 1341

#include <iostream>

int main()
{
    int x = 3;
    auto f = [&x]() { std::cout << x << std::endl; };
    x++;
    f();
}

Upvotes: 4

Related Questions