Home > Mobile >  Why is "this" in lambda capture treated differently than pointer to the same object?
Why is "this" in lambda capture treated differently than pointer to the same object?

Time:12-20

If I capture the "this"-ptr in a lambda I can call member functions without problems. However, when I instead capture the pointer explicitely (without mentioning "this"), it stops working. Am I doing something wrong? According to my understanding, the pointers should be the very same, so this really surprised me. Is there some compiler magic going on to treat "this" in a special way?

(ptr); auto lambda = [conv_ptr, other = std::move(str)]() mutable { foo(std::move(other)); }(); } }; int main() { client c1; c1.local(); client::external(static_cast(&c1)); } '),l:'5',n:'0',o:'C++ source #1',t:'0')),k:48.825065274151434,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((g:!((h:compiler,i:(compiler:g122,deviceViewOpen:'1',filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c++,libs:!(),options:'-Wall -Os --std=gnu++20',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:' x86-64 gcc 12.2 (Editor #1)',t:'0')),header:(),l:'4',m:41.3677130044843,n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compilerName:'x86-64 gcc 12.2',editorid:1,fontScale:14,fontUsePx:'0',j:1,wrap:'1'),l:'5',n:'0',o:'Output of x86-64 gcc 12.2 (Compiler #1)',t:'0')),k:50,l:'4',m:58.632286995515706,n:'0',o:'',s:0,t:'0')),k:51.174934725848566,l:'3',n:'0',o:'',t:'0')),l:'2',n:'0',o:'',t:'0')),version:4" rel="nofollow noreferrer">Demo

#include <cstdio>
#include <string>

struct client
{

    auto foo(std::string&& other)
    {
        printf("%s!\n", other.data());
    }

    void local()
    {
        std::string str = "Hello World this is a sentence to long for sso!";
        auto lambda = [this, other = std::move(str)]() mutable {
            foo(std::move(other));
        }();
    }

    static auto external(void* ptr) {
        std::string str = "Hello World this is a sentence to long for sso!";

        client* conv_ptr = static_cast<client*>(ptr);
        auto lambda = [conv_ptr, other = std::move(str)]() mutable {
            foo(std::move(other));
        }();
    }
};

int main()
{
    client c1;
    c1.local();

    client::external(static_cast<void*>(&c1));
}

Yields:

<source>:15:14: error: 'void lambda' has incomplete type
   15 |         auto lambda = [this, other = std::move(str)]() mutable {
      |              ^~~~~~
<source>: In lambda function:
<source>:25:16: error: cannot call member function 'auto client::foo(std::string&&)' without object
   25 |             foo(std::move(other));
      |             ~~~^~~~~~~~~~~~~~~~~~

CodePudding user response:

  1. The return type of client::foo is void, and you're assigning the return values of it to the two lambdas. The variable name lambda is confusing, since the right hand side of the assignment is actually an immediately invoked lambda expression, not just a lambda.

  2. In the second lambda, since this is not captured, the compiler does not have a way to call foo anymore. Since the intention is to call foo via the pointer conv_ptr, use it explicitly.

    To quote from Lambda expressions on cppreference:

    For the purpose of name lookup, determining the type and value of the this pointer and for accessing non-static class members, the body of the closure type's function call operator or operator template is considered in the context of the lambda-expression.

    Therefore, for class members that are accessible by this->member_name, the this-> part can be omitted, as it can also be omitted in the context of the lambda-expression, which is within a member function of the class. However, this does not apply to the conv_ptr pointer, since conv_ptr->foo() does not mean the same thing as foo() (which is equivalent to this->foo()) in the context.

To make the code compile, remove the invocations () from both lambdas, and call foo using conv_ptr->foo.

#include <string>

struct client
{

    auto foo(std::string&& other)
    {
        printf("%s!\n", other.data());
    }

    void local()
    {
        std::string str = "Hello World this is a sentence to long for sso!";
        auto lambda = [this, other = std::move(str)]() mutable {
            foo(std::move(other));
        };
    }

    static auto external(void* ptr) {
        std::string str = "Hello World this is a sentence to long for sso!";

        client* conv_ptr = static_cast<client*>(ptr);
        auto lambda = [conv_ptr, other = std::move(str)]() mutable {
            conv_ptr->foo(std::move(other));
        };
    }
};

int main()
{
    client c1;
    c1.local();

    client::external(static_cast<void*>(&c1));
}

Try it on godbolt

  • Related