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. `this` inside lambda body
Forum Updated to NodeBB v4.3 + New Features

`this` inside lambda body

Scheduled Pinned Locked Moved Solved C++ Gurus
8 Posts 2 Posters 1.9k Views 3 Watching
  • 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.
  • JonBJ Offline
    JonBJ Offline
    JonB
    wrote on last edited by JonB
    #1

    Normally you can use a lambda where you could use a function/method and they work in essentially the same way as each other, they are interchangeable. There is something about this for the body of the lambda that I cannot quite get my ahead around.

    slotObject->setObjectName("slotObject");
    connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod);
    
    void SlotClass::slotMethod()
    {
        qDebug() << this->objectName();
    }
    

    slotObject is the this in the body of SlotClass::slotMethod() --- connect() uses something like std::function() to set up to call as slotObject->slotMethod().

    Why is this not the case for a lambda slot?

    connect(signalObject, &SignalClass::signalMethod, slotObject, []() { qDebug() << this->objectName(); } );
    

    won't work (or compile) as-is. It is not doing some equivalent of slotObject->lambda().

    While &SlotClass::slotMethod is a member method, and is called with slotObject as this, the lambda is a free function. How does this come about? Does connect() recognise this and have a different code path to implement? Does std::function() (if that's what connect() uses) handle this?

    Chris KawaC 2 Replies Last reply
    0
    • JonBJ JonB

      @Chris-Kawa said in `this` inside lambda body:

      Also, because I didn't mention it above, slotObject in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda

      Hmm, ISTM that is not true. In the case of a member method (not lambda), for connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod); the code will effectively run slotObject->slotMethod(). OIC, here you say "in case of free functions or lambdas", yes in those cases (instead of member method). That is what I was asking about, why it was different.

      So you can pass what you like for the context to a lambda, but you cannot make a lambda act as an implicit member method even if you want to, it's stuck as a free function, is that the case?

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

      @JonB said:

      Hmm, ISTM that is not true.

      Yes, I said the object is not related to free functions/lambdas. In case of member functions absolutely yes, it does double duty as the scope object and the object the member function is executed on.

      you cannot make a lambda act as a member method even if you want to, it's stuck as a free function, is that the case?

      Yes. Lambdas are a bit weird beasts. Like the dual nature of light they have a double identity - they behave like free functions and even decay to a free function pointer, but they are also like an unnamed class with () operator in that they store captured values in their scope, like class members. But they have no explicit type and they don't have their own this. When you invoke a lambda there is no hidden 'this' parameter passed like in case of class member functions.

      Unlike some other languages C++ doesn't allow you to modify the class at runtime, i.e. you can't tack on a random function to a class instance. That also goes for lambdas. You can't do obj->someFunctionThatIsNotAMember() or obj->someLambda and have them get obj as this. As always there are standard proposals though (I doubt they will ever get accepted, but who knows) :)

      JonBJ 1 Reply Last reply
      0
      • JonBJ JonB

        Normally you can use a lambda where you could use a function/method and they work in essentially the same way as each other, they are interchangeable. There is something about this for the body of the lambda that I cannot quite get my ahead around.

        slotObject->setObjectName("slotObject");
        connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod);
        
        void SlotClass::slotMethod()
        {
            qDebug() << this->objectName();
        }
        

        slotObject is the this in the body of SlotClass::slotMethod() --- connect() uses something like std::function() to set up to call as slotObject->slotMethod().

        Why is this not the case for a lambda slot?

        connect(signalObject, &SignalClass::signalMethod, slotObject, []() { qDebug() << this->objectName(); } );
        

        won't work (or compile) as-is. It is not doing some equivalent of slotObject->lambda().

        While &SlotClass::slotMethod is a member method, and is called with slotObject as this, the lambda is a free function. How does this come about? Does connect() recognise this and have a different code path to implement? Does std::function() (if that's what connect() uses) handle this?

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

        @JonB There are two different connect overloads for member and non-member functions. In case of a member function connect stores the object and a function pointer. In case of non-member it's just a function pointer.
        As to this - connect doesn't do anything special about it. It's easier to understand without connect.

        Take this example:

        void SomeClass::someOtherFunction()
        {
            // Connect stores object and method for member functions
            SomeClass* object = this;
            void (SomeClass::* method)() = &SomeClass::someFunction;
        
            // And calls them like this when executed
            (object->*method)();
        }
        

        For free functions it's just:

        // Store just the pointer
        void (*function)() = &someFunction;
        
        // And execute
        function();
        

        Lambdas are (for the purpose of this exercise) just like free functions, except you can't specify the type, so auto:

        // Store just the pointer
        auto function = [](){};
        
        // And execute
        function();
        

        Since lambdas behave like functions you can't use this in them. Like in free functions there is no associated object.
        If you want to use member functions inside the lambda you have to copy/reference the associated object in the catch clause.

        The very explicit version that names the captured object "Obj":

        auto function = [Obj=this](){ qDebug() << Obj->objectName(); }
        

        or you can just name it "this":

        auto function = [this](){ qDebug() << this->objectName(); }
        

        which is a shorthand that creates a variable named this and assigns it the value of this from outer scope.
        You can also capture it implicitly:

        auto function = [=](){ qDebug() << this->objectName(); }
        

        The only special property of this inside a lambda, compared to any other variable, is that once you capture it you can skip naming it in the body, so:

        auto function = [=](){ qDebug() << objectName(); }
        

        but that's just syntax sugar. Other than that it's just another regular variable copied in the capture clause.

        AFAIK connect doesn't use std::function inside (it predates it), but an equivalent implementation, optimized for use with QObjects.

        Also keep in mind that connect is very Qt specific, so it might not actually store member pointers, but something like a meta method id or something. I didn't look too deep at the implementation, but in any case it's something equivalent to function pointer, just optimized for given case.

        JonBJ 1 Reply Last reply
        3
        • JonBJ JonB

          Normally you can use a lambda where you could use a function/method and they work in essentially the same way as each other, they are interchangeable. There is something about this for the body of the lambda that I cannot quite get my ahead around.

          slotObject->setObjectName("slotObject");
          connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod);
          
          void SlotClass::slotMethod()
          {
              qDebug() << this->objectName();
          }
          

          slotObject is the this in the body of SlotClass::slotMethod() --- connect() uses something like std::function() to set up to call as slotObject->slotMethod().

          Why is this not the case for a lambda slot?

          connect(signalObject, &SignalClass::signalMethod, slotObject, []() { qDebug() << this->objectName(); } );
          

          won't work (or compile) as-is. It is not doing some equivalent of slotObject->lambda().

          While &SlotClass::slotMethod is a member method, and is called with slotObject as this, the lambda is a free function. How does this come about? Does connect() recognise this and have a different code path to implement? Does std::function() (if that's what connect() uses) handle this?

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

          @JonB Also, because I didn't mention it above, slotObject in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda. It's not "passed" to the lambda (as this or otherwise) or anything like that. There's really no mechanism in C++ that would allow injecting it into the lambda after it is specified by the user.

          JonBJ 1 Reply Last reply
          2
          • Chris KawaC Chris Kawa

            @JonB There are two different connect overloads for member and non-member functions. In case of a member function connect stores the object and a function pointer. In case of non-member it's just a function pointer.
            As to this - connect doesn't do anything special about it. It's easier to understand without connect.

            Take this example:

            void SomeClass::someOtherFunction()
            {
                // Connect stores object and method for member functions
                SomeClass* object = this;
                void (SomeClass::* method)() = &SomeClass::someFunction;
            
                // And calls them like this when executed
                (object->*method)();
            }
            

            For free functions it's just:

            // Store just the pointer
            void (*function)() = &someFunction;
            
            // And execute
            function();
            

            Lambdas are (for the purpose of this exercise) just like free functions, except you can't specify the type, so auto:

            // Store just the pointer
            auto function = [](){};
            
            // And execute
            function();
            

            Since lambdas behave like functions you can't use this in them. Like in free functions there is no associated object.
            If you want to use member functions inside the lambda you have to copy/reference the associated object in the catch clause.

            The very explicit version that names the captured object "Obj":

            auto function = [Obj=this](){ qDebug() << Obj->objectName(); }
            

            or you can just name it "this":

            auto function = [this](){ qDebug() << this->objectName(); }
            

            which is a shorthand that creates a variable named this and assigns it the value of this from outer scope.
            You can also capture it implicitly:

            auto function = [=](){ qDebug() << this->objectName(); }
            

            The only special property of this inside a lambda, compared to any other variable, is that once you capture it you can skip naming it in the body, so:

            auto function = [=](){ qDebug() << objectName(); }
            

            but that's just syntax sugar. Other than that it's just another regular variable copied in the capture clause.

            AFAIK connect doesn't use std::function inside (it predates it), but an equivalent implementation, optimized for use with QObjects.

            Also keep in mind that connect is very Qt specific, so it might not actually store member pointers, but something like a meta method id or something. I didn't look too deep at the implementation, but in any case it's something equivalent to function pointer, just optimized for given case.

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

            @Chris-Kawa said in `this` inside lambda body:

            There are two different connect overloads for member and non-member functions

            Yep, that would indeed explain.

            All your examples with passing [this] or [=] are not the same/what I was asking about. Those pass the this from the calling method, which obviously is quite different from the slotObject which is in the connect() and which I was asking about being the this inside the lambda body.

            So summing up I think you are saying that a lambda is always a free function/functor, it never acts as a member function. You can explicitly pass in [this] or [=] as context but there is something different about that.

            Chris KawaC 1 Reply Last reply
            0
            • JonBJ JonB

              @Chris-Kawa said in `this` inside lambda body:

              There are two different connect overloads for member and non-member functions

              Yep, that would indeed explain.

              All your examples with passing [this] or [=] are not the same/what I was asking about. Those pass the this from the calling method, which obviously is quite different from the slotObject which is in the connect() and which I was asking about being the this inside the lambda body.

              So summing up I think you are saying that a lambda is always a free function/functor, it never acts as a member function. You can explicitly pass in [this] or [=] as context but there is something different about that.

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

              @JonB Yup, in case of lambdas the parameter would be better named scopeObject or something like that, as it's not directly related to the passed function/lambda.

              1 Reply Last reply
              2
              • Chris KawaC Chris Kawa

                @JonB Also, because I didn't mention it above, slotObject in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda. It's not "passed" to the lambda (as this or otherwise) or anything like that. There's really no mechanism in C++ that would allow injecting it into the lambda after it is specified by the user.

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

                @Chris-Kawa said in `this` inside lambda body:

                Also, because I didn't mention it above, slotObject in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda

                Hmm, ISTM that is not true. In the case of a member method (not lambda), for connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod); the code will effectively run slotObject->slotMethod(). OIC, here you say "in case of free functions or lambdas", yes in those cases (instead of member method). That is what I was asking about, why it was different.

                So you can pass what you like for the context to a lambda, but you cannot make a lambda act as an implicit member method even if you want to, it's stuck as a free function, is that the case?

                Chris KawaC 1 Reply Last reply
                0
                • JonBJ JonB

                  @Chris-Kawa said in `this` inside lambda body:

                  Also, because I didn't mention it above, slotObject in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda

                  Hmm, ISTM that is not true. In the case of a member method (not lambda), for connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod); the code will effectively run slotObject->slotMethod(). OIC, here you say "in case of free functions or lambdas", yes in those cases (instead of member method). That is what I was asking about, why it was different.

                  So you can pass what you like for the context to a lambda, but you cannot make a lambda act as an implicit member method even if you want to, it's stuck as a free function, is that the case?

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

                  @JonB said:

                  Hmm, ISTM that is not true.

                  Yes, I said the object is not related to free functions/lambdas. In case of member functions absolutely yes, it does double duty as the scope object and the object the member function is executed on.

                  you cannot make a lambda act as a member method even if you want to, it's stuck as a free function, is that the case?

                  Yes. Lambdas are a bit weird beasts. Like the dual nature of light they have a double identity - they behave like free functions and even decay to a free function pointer, but they are also like an unnamed class with () operator in that they store captured values in their scope, like class members. But they have no explicit type and they don't have their own this. When you invoke a lambda there is no hidden 'this' parameter passed like in case of class member functions.

                  Unlike some other languages C++ doesn't allow you to modify the class at runtime, i.e. you can't tack on a random function to a class instance. That also goes for lambdas. You can't do obj->someFunctionThatIsNotAMember() or obj->someLambda and have them get obj as this. As always there are standard proposals though (I doubt they will ever get accepted, but who knows) :)

                  JonBJ 1 Reply Last reply
                  0
                  • Chris KawaC Chris Kawa

                    @JonB said:

                    Hmm, ISTM that is not true.

                    Yes, I said the object is not related to free functions/lambdas. In case of member functions absolutely yes, it does double duty as the scope object and the object the member function is executed on.

                    you cannot make a lambda act as a member method even if you want to, it's stuck as a free function, is that the case?

                    Yes. Lambdas are a bit weird beasts. Like the dual nature of light they have a double identity - they behave like free functions and even decay to a free function pointer, but they are also like an unnamed class with () operator in that they store captured values in their scope, like class members. But they have no explicit type and they don't have their own this. When you invoke a lambda there is no hidden 'this' parameter passed like in case of class member functions.

                    Unlike some other languages C++ doesn't allow you to modify the class at runtime, i.e. you can't tack on a random function to a class instance. That also goes for lambdas. You can't do obj->someFunctionThatIsNotAMember() or obj->someLambda and have them get obj as this. As always there are standard proposals though (I doubt they will ever get accepted, but who knows) :)

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

                    @Chris-Kawa said in `this` inside lambda body:

                    That also goes for lambdas. You can't do obj->someFunctionThatIsNotAMember() or obj->someLambda and have them get obj as this. As always there are standard proposals though (I doubt they will ever get accepted, but who knows) :)

                    Yep, that clarifies! I thought there might be a way that you could do that, but apparently not from C++.

                    Thanks for your responses.

                    1 Reply Last reply
                    0
                    • JonBJ JonB has marked this topic as solved on

                    • Login

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