2

Is there any way to create variable that will be unique for some lambda function and will last between launches of lambda?
More careful description: I want lambda with variable initialized to some value, and that variable should last between launches:

std::function<void(void)> a=[]()
{
    /*here we declare variable X and initialize it to 0*/;
    std::cout<<X++;
};
a();a();

So this should print out 01

But also I need to be sure that "X" is unique for "a", so after previous part this

std::function<void(void)> b=a;
b();b();

should print out 01.

I tried using static variables, but they are shared between copies(so these two parts print out 0123).

So, is there any way to do it?

3
  • 1
    Why complicate? Can't you write a class? Commented Mar 31, 2016 at 15:48
  • I have somewhat complex system of classes, one of them uses lamdas as custom functions defined by user. And I want to avoid using abstract classes for purposes of data storage for these functions. And also objects of this class can be copied, so simple solutions like static variables doesn't work. Commented Mar 31, 2016 at 15:55
  • That sort of breaks what "copying" means. Commented Mar 31, 2016 at 16:09

3 Answers 3

3

I don't think mutable lambdas are sufficient. The mutable capture will get copied, when you copy the function pointer, also copying the counter. My read of the question, is that each copy of the lambda should start with the initial mutable capture.

You need to capture a custom class, with a copy constructor, to do this:

#include <functional>
#include <iostream>

class my_class {

public:
    int n=0;

    my_class()
    {
    }

    my_class(const my_class &b)
    {
    }
};

int main()
{

    std::function<void(void)> a=
        [my_class_instance=my_class()]()
        mutable
        {
            std::cout << my_class_instance.n++;
        };
    a();
    a();

    auto b=a;

    b();
    b();

}

The result from this is:

0101

Without a helper class, the equivalent code using only mutable lambdas will produce a

0123

My read of the question, is that the former behavior is desired.

Sign up to request clarification or add additional context in comments.

1 Comment

It's possible that if the class member is declared mutable, you don't even need to make the lambda itself use mutable captures. Too lazy to confirm this, but sounds right to me, too...
2

You want it to reset on copies. Make data that does this:

template<class T>
struct no_copy {
  T init;
  T current;
  operator T&(){ return current; }
  operator T const&()const{ return current; }
  no_copy( T&& i ):init(i), current(init) {}
  no_copy( no_copy const&o ):init(o.init), current(init) {}
  no_copy( no_copy &&o ):init(std::move(o.init)), current(init) {}
};
template<class T>
no_copy<std::decay_t<T>> make_no_copy(T&& t){
  return {std::forward<T>(t)};
}

Then, in C++14, easy:

std::function<void(void)> a=[X=make_no_copy(0)]()mutable
{
  std::cout<<X++;
};
a();a();

prints out 01.

In C++11:

auto X=make_no_copy(0);
std::function<void(void)> a=[X]()mutable
{
  std::cout<<X++;
};
a();a();

it also works, but is a bit more ugly.

Other than a copy of X existing outside of the lambda, the C++11 version is the same as the C++14 version in behavior.

live example

3 Comments

I don't think that's exactly what OP wants. Now, add this part, after a(); a(); "auto b=a; b(); b()". The OP wants b's counter reset, based on my read of the question.
OP wants the member to be reset when function object is copied.
@sam ah, missed that quirk. Type added to make that happen.
0

Is using the copy constructor for "resetting" the only option? Shouldn't you be instead writing a factory function that emits fresh lambdas from the same initial environment?

Expecting that stuff A is different from stuff B after a copy is abuse of semantics.

auto make_counter() -> std::function<int()> {
    return [x=0]() mutable { return x++; };
}

auto a = make_counter();
std::cout << a() << " " << a() << "\n";

auto b = make_counter();
std::cout << b() << " " << b() << "\n";

4 Comments

The question explicitly requires the counter to be reset automatically, when the type-erased std::function gets copied, instead of constructing another instance of the lambda. Whether or not it's "abuse of semantics", or not, is somewhat beside the point. Maybe it is, maybe it's not, it's for someone else to argue. But, it's an interesting technical question, that's valid on its merits.
@SamVarshavchik "The question explicitly requires" – I know. If OP asked for advice on how to commit suicide, would you provide that advice? Or would you rather tell him not to do it? All I want to prevent is answering the Y of an XY problem.
Comparing a technical question with suicide is a bit of a stretch.
@SamVarshavchik it may be, but the point is: the reason why I didn't answer the exact technical question is because it smells and stinks, and there's a 99% chance my suggestion constitutes a cleaner solution to the problem OP is actually trying to solve.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.