Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. static in lambda

static in lambda

Scheduled Pinned Locked Moved Unsolved C++ Gurus
18 Posts 5 Posters 2.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Chris KawaC Chris Kawa

    @JonB For the purpose of this example think of a lambda as a class with operator(). That static is just a normal static variable in member function scope so it behaves like such. It is accessible as long as instance of that lambda exists so as long as the connection does.

    If you wrote a separate connect with exactly the same lambda it would be a separate static, because each lambda (even if identical) is a separate "class" to the compiler. If you intended to reuse the same static you'd need to assign that lambda to na auto variable and reuse that in both connects.

    @KroMignon Lambdas in VS2013 are buggy and all sorts of non-conformant to the standard. Expect it to misbehave and definitely don't assume anything about the standard based on that compiler.

    JonBJ Offline
    JonBJ Offline
    JonB
    wrote on last edited by JonB
    #9

    @Chris-Kawa
    I don't understand enough of what you are saying, but from reading around I'm beginning to think that a static local variable in a lambda is equivalent to taking it outside the lambda, putting it into the calling function, and passing it by reference. So here:

    static qint64 bytesSend{0};
    QObject::connect(..., [&bytesSend]());
    

    In which case, if this connect() is called more than once they will all share the same bytesSend and code would not behave as intended (which is to count bytes sent on one socket).

    ?

    1 Reply Last reply
    0
    • J.HilkJ J.Hilk

      @JonB to be more confusing, you do not have to use a static at all let me introduce you to:

          QCoreApplication app(argc, argv);
      
          auto bytesWritten = [bytesToSend = 64 ](int bytes)mutable ->void{
              bytesToSend -= bytes;
              if(bytesToSend <= 0){
                  qDebug() <<"finished";
                  qApp->exit();
              }
          };
      
          for( int i{0}; i < 8; i++){
              bytesWritten(8);
          }
          return app.exec();
      
      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by
      #10

      @J-Hilk said in static in lambda:

      auto bytesWritten = [bytesToSend = 64 ](int bytes)mutable ->void{

      Same question! Is that bytesToSend a separate variable for each call of this line or the same one? If it's separate, is that really the same as using static, as per my previous post just above? :)

      1 Reply Last reply
      0
      • J.HilkJ J.Hilk

        @JonB to be more confusing, you do not have to use a static at all let me introduce you to:

            QCoreApplication app(argc, argv);
        
            auto bytesWritten = [bytesToSend = 64 ](int bytes)mutable ->void{
                bytesToSend -= bytes;
                if(bytesToSend <= 0){
                    qDebug() <<"finished";
                    qApp->exit();
                }
            };
        
            for( int i{0}; i < 8; i++){
                bytesWritten(8);
            }
            return app.exec();
        
        Chris KawaC Offline
        Chris KawaC Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on last edited by Chris Kawa
        #11

        @JonB No, that's not how it works. It's closer to:

        class SomeDummyClass
        {
           static qint64 bytesSend;
           operator()(int bytes) { ... }
        } dummy_instance;
        
        connect(..., &dummy_instance, SomeDummyClass::operator())
        

        so this will have separate statics:

        connect(..., []{ static int foo; });
        connect(..., []{ static int foo; });
        

        and this will share one:

        auto func = []{ static int foo; };
        connect(..., func);
        connect(..., func);
        

        @J-Hilk Just to note - this requires C++14 but is a great replacement for such statics.

        @JonB said:

        Is that bytesToSend a separate variable for each call of this line or the same one?

        It's equivalent to defining a non-static member in that class above so it's unique to an instance of the lambda.

        JonBJ 1 Reply Last reply
        3
        • Chris KawaC Chris Kawa

          @JonB No, that's not how it works. It's closer to:

          class SomeDummyClass
          {
             static qint64 bytesSend;
             operator()(int bytes) { ... }
          } dummy_instance;
          
          connect(..., &dummy_instance, SomeDummyClass::operator())
          

          so this will have separate statics:

          connect(..., []{ static int foo; });
          connect(..., []{ static int foo; });
          

          and this will share one:

          auto func = []{ static int foo; };
          connect(..., func);
          connect(..., func);
          

          @J-Hilk Just to note - this requires C++14 but is a great replacement for such statics.

          @JonB said:

          Is that bytesToSend a separate variable for each call of this line or the same one?

          It's equivalent to defining a non-static member in that class above so it's unique to an instance of the lambda.

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #12

          @Chris-Kawa
          Hmm, OK, then from what you are saying @J-Hilk is good, they will be separate statics....

          But some post I was reading over at stackoverflow was saying something like: "Because the lambda definitions are identical in multiple calls, this means the static variable is as though it lives in the calling function and is passed (by reference) to the lambda", as per my code interpretation above. Which you are saying is not correct....

          So I wonder where the separate instances of the static variable are actually stored, as we need a distinct one for each invocation....?

          1 Reply Last reply
          0
          • Chris KawaC Offline
            Chris KawaC Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on last edited by Chris Kawa
            #13

            @JonB Well lambdas are their own thing so neither your nor mine is 100% correct, but mine is closer to how it works in terms of encapsulation and lifetime.
            What @J-Hilk wrote is good and it doesn't use statics. A variable defined in the capture clause is a non-static member variable in that dummy class, so something along the lines of:

            class SomeDummyClass
            {
               qint64 bytesSend = 64;
               operator()(int bytes) { ... }
            } dummy_instance;
            

            The statics are part of that dummy class, so follow the rules of the usual static class members. A lambda defined inline in the connect is just instantiating that dummy class, so like someFunction(QString()) - creating local instance in place. The difference is that lambdas don't have user visible type, so the DummyClass is "private" and exposed only internally to the compiler, so while this:

            someFunc(QString());
            someFunc(QString());
            

            would share a static between different QString instances does not apply to lambdas, becase this:

            someFunc([]{});
            someFunc([]{});
            

            is to the compiler this:

            someFunc(SomeDummyClass1());
            someFunc(SomeDummyClass2());
            

            so each lambda is its own separate class.

            JonBJ 1 Reply Last reply
            1
            • Chris KawaC Chris Kawa

              @JonB Well lambdas are their own thing so neither your nor mine is 100% correct, but mine is closer to how it works in terms of encapsulation and lifetime.
              What @J-Hilk wrote is good and it doesn't use statics. A variable defined in the capture clause is a non-static member variable in that dummy class, so something along the lines of:

              class SomeDummyClass
              {
                 qint64 bytesSend = 64;
                 operator()(int bytes) { ... }
              } dummy_instance;
              

              The statics are part of that dummy class, so follow the rules of the usual static class members. A lambda defined inline in the connect is just instantiating that dummy class, so like someFunction(QString()) - creating local instance in place. The difference is that lambdas don't have user visible type, so the DummyClass is "private" and exposed only internally to the compiler, so while this:

              someFunc(QString());
              someFunc(QString());
              

              would share a static between different QString instances does not apply to lambdas, becase this:

              someFunc([]{});
              someFunc([]{});
              

              is to the compiler this:

              someFunc(SomeDummyClass1());
              someFunc(SomeDummyClass2());
              

              so each lambda is its own separate class.

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by
              #14

              @Chris-Kawa said in static in lambda:

              What @J-Hilk wrote is good and it doesn't use statics.

              I was (originally) asking about his original code, which does use the static.

              so each lambda is its own separate class.

              I don't doubt that you are correct, the stackoverflow post was claiming that because the lambdas were identical (being invoked from the same line of code) this made them share the same, one class, like

              someFunc(SomeDummyClass1());
              someFunc(SomeDummyClass1());
              

              Maybe I misunderstood what I read there!

              Chris KawaC 1 Reply Last reply
              1
              • JonBJ JonB

                @Chris-Kawa said in static in lambda:

                What @J-Hilk wrote is good and it doesn't use statics.

                I was (originally) asking about his original code, which does use the static.

                so each lambda is its own separate class.

                I don't doubt that you are correct, the stackoverflow post was claiming that because the lambdas were identical (being invoked from the same line of code) this made them share the same, one class, like

                someFunc(SomeDummyClass1());
                someFunc(SomeDummyClass1());
                

                Maybe I misunderstood what I read there!

                Chris KawaC Offline
                Chris KawaC Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on last edited by Chris Kawa
                #15

                @JonB Maybe they meant the case with lambda reuse via variable, like I posted above. This

                someFunc(SomeDummyClass1());
                someFunc(SomeDummyClass1());
                

                is simply not possible because you don't have access to the class, so you can't reuse it. You can only reuse an instance of that class (i.e. the auto variable). Think of lambdas as class that can only be instantiated once.

                It's to the point that this won't work:

                auto foo = []{};
                auto bar = decltype(foo)();
                

                i.e. you can't try to query lambdas type and then create another instance of it.

                JonBJ 1 Reply Last reply
                0
                • Chris KawaC Chris Kawa

                  @JonB Maybe they meant the case with lambda reuse via variable, like I posted above. This

                  someFunc(SomeDummyClass1());
                  someFunc(SomeDummyClass1());
                  

                  is simply not possible because you don't have access to the class, so you can't reuse it. You can only reuse an instance of that class (i.e. the auto variable). Think of lambdas as class that can only be instantiated once.

                  It's to the point that this won't work:

                  auto foo = []{};
                  auto bar = decltype(foo)();
                  

                  i.e. you can't try to query lambdas type and then create another instance of it.

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by JonB
                  #16

                  @Chris-Kawa
                  Would you care to have a look at https://stackoverflow.com/a/60592393/489865. The interesting bit (excerpt):

                  void bar() {
                     auto f = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                     f();
                     f();
                  }
                  
                  void test() {
                     bar();
                     bar();  // k is persistent through lambda instantiations
                     return 0;
                  }
                  

                  Is this correct? Am I misunderstanding, or does this mean one instance of static int k shared across multiple invocations? Wouldn't that make @J-Hilk's original code (at potentially) wrong, as separate sockets would all shre the same bytes-written count? Or, is this behaviour to do with the fact that in this example the lambda is stored in a variable auto f, and that makes it different from him passing it inline to connect()?

                  My brain hurts! This was suppsoed to eb a quick question, I'm spending all morning discussing it :)

                  1 Reply Last reply
                  0
                  • Chris KawaC Offline
                    Chris KawaC Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on last edited by
                    #17

                    @JonB Yeah, I guess I went too far in my class analogy :) That's because lambdas are a little different and it's not exactly that there is only one instance of it. It's more that the compiler is the only one allowed to name its type, not user in their code. So you're right - this example will share the static because the type of the lambda is the same in this case. Note however that it's not the same as

                    auto f = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                    f();
                    auto f2 = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                    f2();
                    

                    these lambdas, although look the same are of different types internally.

                    JonBJ 1 Reply Last reply
                    0
                    • Chris KawaC Chris Kawa

                      @JonB Yeah, I guess I went too far in my class analogy :) That's because lambdas are a little different and it's not exactly that there is only one instance of it. It's more that the compiler is the only one allowed to name its type, not user in their code. So you're right - this example will share the static because the type of the lambda is the same in this case. Note however that it's not the same as

                      auto f = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                      f();
                      auto f2 = []() { static int k = 1; cout << k++ << "\n";}; // define k as static
                      f2();
                      

                      these lambdas, although look the same are of different types internally.

                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on last edited by JonB
                      #18

                      @Chris-Kawa said in static in lambda:

                      these lambdas, although look the same are of different types internally.

                      Yes, that I do realise! Just as in C/C++ struct { int foo; } and another struct { int foo; } are not the same type. (But I think they are in Pascal-like languages....)

                      So you're right - this example will share the static because the type of the lambda is the same in this case

                      Which I think would make @J-Hilk's original not behave as anticipated if more than connect() is made! :)

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved