gpt4 book ai didi

c++ - 可以在可变 lambda 中更改 `this` 吗?

转载 作者:行者123 更新时间:2023-12-04 05:37:30 25 4
gpt4 key购买 nike

大家都知道this C++ 中的对象指针不能在其方法中更改。但是对于可变的 lambdas 来说,this被捕获,一些当前的编译器给出了这种可能性。考虑这个代码:

struct A {
void foo() {
//this = nullptr; //error everywhere

(void) [p = this]() mutable {
p = nullptr; //#1: ok everywhere
(void)p;
};

(void) [this]() mutable {
this = nullptr; //#2: ok in MSVC only
};
}
};

在第一个 lambda this 中被捕获并赋予新名称 p .这里所有的编译器都允许改变 p 的值.在第二个 lambda this 中被自己的名字捕获,只有 MSVC 允许程序员改变它的值。演示: https://gcc.godbolt.org/z/x5P81TT4r
我相信 MSVC 在第二种情况下行为不正确(尽管它看起来像一个不错的语言扩展)。任何人都可以从标准中找到正确的措辞(搜索并不容易,因为 this 这个词在那里被提及了 2800 多次)?

最佳答案

this 的初始捕获

(void) [p = this]() mutable { 
p = nullptr; //#1: ok everywhere
(void)p;
};

这使用 init-capture 来捕获 this按值指针,根据 [expr.prim.lambda.capture]/6 ,表示它是 this 的拷贝指针。在 this 的上下文中是 const - 合格,拷贝自然不能用来改 this (即使 lambda 是可变的;与“指向常量”进行比较),但由于 lambda 是可变的,因此指针(拷贝)可用于指向不同的东西,例如 nullptr .
struct S {
void f() const {
(void) [p = this]() mutable {
p->value++; // ill-formed: 'this' is pointer to const, meaning
// 'p' is pointer to const.
p = nullptr; // OK: 'p' is not const pointer
(void)p;
};
}

void f() {
(void) [p = this]() mutable {
p->value++; // OK: 'this' is pointer to non-const, meaning
// 'p' is pointer to non-const.
p = nullptr; // OK: 'p' is not const pointer
(void)p;
};
}
int value{};
};
this 的简单捕获
(void) [this]() mutable { 
this = nullptr; //#2: ok in MSVC only
};

根据 [expr.prim.lambda.capture] , 忽略 capture-default:s 的情况:
  • 捕获列表包含捕获
  • 捕获是简单捕获或初始化捕获;我们忽略后一种情况,因为它在上面提到过
  • 简单捕获具有以下形式之一:
  • 标识符 ...选择
  • &标识符 ...选择
  • this
  • *this


  • 根据 [expr.prim.lambda.capture]/10 [ 重点 我的]:

    An entity is captured by copy if

    • (10.1) it is implicitly captured, the capture-default is =, and the captured entity is not *this, or

    • (10.2) it is explicitly captured with a capture that is not of the form this, & identifier, or & identifier initializer.


    只有简单的捕获形式 *this允许显式捕获 *this通过复制对象。简单捕获 this但是,捕获 *this object(+) 通过引用,根据 [expr.prim.lambda.capture]/12 :
    (+) 简单捕获:s this*this两者都表示本地实体 *this ,根据 [expr.prim.lambda.capture]/4 .

    An entity is captured by reference if it is implicitly orexplicitly captured but not captured by copy. It is unspecified whether additional unnamed non-static data members are declared in theclosure type for entities captured by reference. [...]


    因此:
    struct S {
    void f() const {
    (void) [this]() mutable {
    // '*this' explicitly-captured by-reference
    this->value++; // ill-formed: 'this' is pointer to const
    this = nullptr; // #2 ill-formed: 'this' is not a modifyable lvalue
    };
    }

    void f() {
    (void) [this]() mutable {
    // '*this' explicitly-captured by-reference
    this->value++; // OK: 'this' is pointer to non-const
    this = nullptr; // #2 ill-formed: 'this' is not a modifyable lvalue
    };
    }
    int value{};
    };
    根据 [class.this]/1 , this不是可修改的左值,这就是为什么 #2格式错误:

    In the body of a non-static ([class.mfct]) member function, the keyword this is a prvalue whose value is a pointer to the object for which the function is called. The type of this in a member function whose type has a cv-qualifier-seq cv and whose class is X is “pointer to cv X”. [...]


    其中,根据 [expr.prim.lambda.closure]/12也适用于 this用于 lambda 表达式:

    The lambda-expression's compound-statement yields the function-body ([dcl.fct.def]) of the function call operator, but for purposes of name lookup, determining the type and value of this and transforming id-expressions referring to non-static class members into class member access expressions using (*this) ([class.mfct.non-static]), the compound-statement is considered in the context of the lambda-expression.


    MSVC 接受您的代码段是不正确的(接受无效)。
    事实上,在下面的例子中( demo ):
    #include <iostream>

    struct S;

    void g(S *& ) { std::cout << "lvalue pointer to S"; }
    void g(S *&& ) { std::cout << "rvalue pointer to S"; }

    struct S {
    void f() {
    auto l = [this]() { g(this); };
    l();
    }
    };

    int main() {
    S s{};
    s.f();
    }
    我们期待第二个 g重载成为更好的匹配,如 this是一个纯右值。然而,虽然 GCC 和 Clang 的行为符合预期
    // GCC & Clang: rvalue pointer to S
    MSVC 甚至无法编译程序
    // MSVC: error, no viable overload; arg list is '(S *const )'
    这违反了 [class.this]/1,因为

    [...] The type of this in a member function whose type has a cv-qualifier-seq cv and whose class is X is “pointer to cv X” [...]


    ... 而不是“指向 cv X 的常量指针”(首先,纯右值上的常量会很奇怪)。

    关于c++ - 可以在可变 lambda 中更改 `this` 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68812672/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com