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 Offline
    Chris KawaC Offline
    Chris Kawa
    Lifetime Qt Champion
    wrote on last edited by
    #7

    @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 1 Reply Last reply
    4
    • JonBJ JonB

      Yes, I could doubtless look this up, but I always welcome explanations from the C++ experts here instead :)

      Friend @J-Hilk offered the following lambda over at https://forum.qt.io/topic/123558/how-do-i-know-that-the-client-has-connected-to-the-server/18

                  QObject::connect(socket, &QTcpSocket::bytesWritten, socket, [socket, bytesToSend = data.size()](qint64 bytes)->void{
                      static qint64 bytesSend{0};
                      bytesSend += bytes;
                      if(static_cast<qint64>(bytesToSend) == bytesSend)
                          socket->close();
                  });
      

      How "safe" is that static qint64 bytesSend{0}; in a lambda? I presume this is correctly supported in C++ as his code intends? "Where" is that static variable stored such that it is "local"/applies only to this lambda? If you were to call this connect code more than once, would it create multiple lambdas, would each one have its own static variable or would they share the same one?

      J.HilkJ Online
      J.HilkJ Online
      J.Hilk
      Moderators
      wrote on last edited by
      #8

      @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();
      

      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


      Q: What's that?
      A: It's blue light.
      Q: What does it do?
      A: It turns blue.

      JonBJ Chris KawaC 2 Replies Last reply
      2
      • 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