3

I have this simple code:

#include <iostream>
#include <functional>

class a {
public:
    a() {
        func = [] {
            static int i = 0;
            i++;
            std::cout << i << std::endl;
        };
    }
    std::function<void()> func;
};

int main()
{
    a a1;
    a1.func();
    a a2;
    a2.func();
}

I expected an output like this:

1
1

But instead it was:

1
2

Then I checked the memory addresses of the lambdas in the two instances of a and they were the same. This means that the lambda is created once and then used in all instances. What I am looking for is to make the constructor create a different lambda every time when it gets called. I turned off the compiler optimizations but there was no effect.

INFO: I am using MSVC.

Ivan Venkov
  • 89
  • 1
  • 7
  • 2
    This looks like an XY problem. Why is `static int i` static, if you do not want shared state ? If you want to store per-instance variables, make them a member of `class a`, and capture that member in the lambda. – MSalters Jan 17 '23 at 11:09
  • @MSalters , the lambda should not capture anything. – Ivan Venkov Jan 17 '23 at 11:14
  • 5
    "The lambda should not capture anything" - Because? This is an arbitrary restriction, only further supporting the fact this is an XY problem. – StoryTeller - Unslander Monica Jan 17 '23 at 11:15
  • @IvanVenkov: Non-capturing lambda's can be converted to ordinary function pointers, which are stateless. IOW, `a::func` can be a `void(*)()` function pointer, and it will always be the same function pointer. That is the automatic result from your requirement that it doesn't capture. So you really have two incompatible requirements here. Which one is real? – MSalters Jan 17 '23 at 11:18
  • So, you have a state and a method that operates on this state, this already sounds like a small class to me (which is what a mutable lambda is, but without a name) – Yksisarvinen Jan 17 '23 at 11:18
  • 1
    An XY problem is asking about a solution you are fixated upon instead of the actual problem. What you *really* want is additional data (or `this`) that is accessible to a `WNDPROC`. Had you tried asking about *that*, something like this [Q&A](https://stackoverflow.com/questions/117792/best-method-for-storing-this-pointer-for-use-in-wndproc) would have popped up. – StoryTeller - Unslander Monica Jan 17 '23 at 11:30
  • [What is the XY problem?](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – 463035818_is_not_an_ai Jan 17 '23 at 12:12

2 Answers2

6

Conceptually, you do have separate instances of the lambda. What you have witnessed might be a compiler optimization, but the actual issue is that static makes i owned by the function body itself, and thus shared by all instances of your lambda.

What you want instead is to make i a member of each lambda instance, which you can do with an extended capture list:

func = [i = 0]() mutable {
    i++;
    std::cout << i << std::endl;
};
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • My purpose is to use a lambda as `WNDPROC` callback. That's why I believed the lambda shouldn't capture anything. Is there a way to convert a capturing lambda to `WNDPROC`? – Ivan Venkov Jan 17 '23 at 11:22
  • @IvanVenkov capturing lambda can be converted to function pointer only by actually wrapping it into function, so if you want to have different functions then you likely need to wrap the lambda calls into function templates. – Öö Tiib Jan 17 '23 at 11:26
  • 1
    Its not possible to create "function" at runtime. And this is what you basically want. If I remember correctly there should be some user data filed which you can use to pass any function object, including lambda, which can be called from single static function. – sklott Jan 17 '23 at 11:27
  • 1
    @IvanVenkov then you should ask a question on what you are actually trying to do, instead of the thing you _think_ a way to go. (the answer is likely a single function keeping per-window data and distinguishing between instances by HWND) – Revolver_Ocelot Jan 17 '23 at 11:30
  • @Revolve_Ocelot exactly. – Ivan Venkov Jan 17 '23 at 11:34
1

If you are on a C++11 only compiler, you can drop the lambda syntax sugar and return to the function objects (which lambdas are a shorthand version of defining)

struct CallCounter
{
    int i = 0;
    void operator()()
    {
        i++;
        std::cout << i << std::endl;
    }
};

class a {
public:
    std::function<void()> func = CallCounter{};
};

Revolver_Ocelot
  • 8,609
  • 3
  • 30
  • 48