为什么 lambda 捕获中的“this”与指向同一对象的指针的处理方式不同?

gla*_*des 1 c++ lambda closures capture this-pointer

如果我在 lambda 中捕获“this”-ptr,我就可以毫无问题地调用成员函数。但是,当我明确捕获指针(不提及“this”)时,它会停止工作。难道我做错了什么?根据我的理解,指针应该是一样的,所以这真的让我很惊讶。是否有一些编译器魔法以特殊的方式处理“this”?

演示

#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));
}
Run Code Online (Sandbox Code Playgroud)

产量:

<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));
      |             ~~~^~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

kot*_*aki 5

  1. 的返回类型client::foovoid,并且您将其返回值分配给两个lambdas。变量名称lambda令人困惑,因为赋值的右侧实际上是立即调用的 lambda 表达式,而不仅仅是 lambda。

  2. 在第二个 lambda 中,由于this没有捕获,编译器没有办法再调用foo。由于目的是foo通过指针调用conv_ptr,因此显式使用它。

    引用cppreference 上的Lambda 表达式

    为了名称查找、确定this指针的类型和值以及访问非静态类成员,闭包类型的函数调用运算符或运算符模板的主体在 lambda 表达式的上下文中考虑。

    因此,对于可通过 访问的类成员this->member_namethis->可以省略该部分,因为它也可以在位于类的成员函数内的 lambda 表达式的上下文中省略。但是,这不适用于指针conv_ptr,因为与上下文中的conv_ptr->foo()含义不同foo()(相当于)。this->foo()

要使代码编译,请()从两个 lambda 中删除调用,然后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));
}
Run Code Online (Sandbox Code Playgroud)

在 Godbolt 上尝试一下