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

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

提问人:glades 提问时间:12/19/2022 更新时间:12/19/2022 访问量:292

问:

如果我在 lambda 中捕获“this”-ptr,我可以毫无问题地调用成员函数。但是,当我显式捕获指针时(不提及“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));
}

收益 率:

<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));
      |             ~~~^~~~~~~~~~~~~~~~~~
C++ lambda 闭包 捕获 此指针

评论

0赞 molbdnilo 12/19/2022
是的,捕获使其隐含在 lambda 中。捕获一些任意指针则不然。this
1赞 glades 12/19/2022
@molbdnilo 如果你能做这样的事情,那就太好了:重新定义闭包内的“这个”是什么[this = conv_ptr, other = std::move(str)](){...}
0赞 Eljay 12/19/2022
该类型是无法完成的不完整类型。因此,不允许使用类型的对象。这就是绊倒错误的原因。voidvoidvoid lambda;

答:

5赞 kotatsuyaki 12/19/2022 #1
  1. 返回类型为 ,您将它的返回值分配给两个 s。变量名称令人困惑,因为赋值的右侧实际上是立即调用的 lambda 表达式,而不仅仅是 lambda。client::foovoidlambdalambda

  2. 在第二个 lambda 中,由于未捕获,编译器无法再调用。由于目的是通过指针调用,因此显式使用它。thisfoofooconv_ptr

    要引用 cpppreference 上的 Lambda 表达式,请执行以下操作:

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

    因此,对于可由 访问的类成员,可以省略该部分,因为在 lambda-expression 的上下文中也可以省略该部分,该函数位于类的成员函数中。但是,这不适用于指针,因为在上下文中与指针的含义不同(等效于 )。this->member_namethis->conv_ptrconv_ptr->foo()foo()this->foo()

要使代码可编译,请从两个 lambda 中删除调用,然后使用 调用。()fooconv_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));
}

在 godbolt 上试用