用相同类型的新对象覆盖对象并使用 this 闭包

发布于 2025-01-11 19:17:33 字数 519 浏览 4 评论 0原文

在以下代码中,一个对象被相同类型的新对象覆盖,其中 lambda 表达式创建一个使用旧对象的 this 的闭包。旧地址 (this) 保持不变,新对象具有相同的布局,所以这应该没问题,而不是 UB。但是对于不平凡的对象或其他情况呢?

struct A {
    void g(A& o, int v) {
        o = A{.x = v, .f = [this]{
            std::cout << "f" << this->x << '\n';
        }};
    }
    int x{0};
    std::function<void()> f;
    ~A() {
        std::cout << "dtor" << x << '\n';
    }
};

void test() {
    A a;
    a.g(a, 2);
    a.f();
}

In the following code an object is overwritten with a new object of same type, where a lambda-expression creates a closure that uses this of the old object. The old address (this) remains the same, the new object has the same layout, so this should be ok and not UB. But what about non trivial objects or other cases?

struct A {
    void g(A& o, int v) {
        o = A{.x = v, .f = [this]{
            std::cout << "f" << this->x << '\n';
        }};
    }
    int x{0};
    std::function<void()> f;
    ~A() {
        std::cout << "dtor" << x << '\n';
    }
};

void test() {
    A a;
    a.g(a, 2);
    a.f();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

GRAY°灰色天空 2025-01-18 19:17:33

您实际上并没有替换任何对象。您只是将另一个对象分配给当前对象。 o = 只是调用隐式复制赋值运算符,该运算符将从使用 A{...} 的赋值表达式构造的临时 A 中复制赋值各个成员。

lambda 将从 g 中的 this 捕获 this,而不是从临时对象。

std::function 将始终保留引用调用 g 的原始对象的 lambda 副本,并且由于那是其父对象,因此它的寿命不能超过它。

所以这里没有问题。唯一的例外是在销毁 A 对象期间调用 f,在这种情况下,可能会禁止使用捕获的指针。

You are not actually replacing any object. You are just assigning from another object to the current one. o = simply calls the implicit copy assignment operator which will copy-assign the individual members from the temporary A constructed in the assignment expression with A{...}.

The lambda is going to capture this from this in g, not from the temporary object.

std::function will always keep a copy of the lambda referring to the original object on which g was called and since that is its parent object, it cannot outlive it.

So there is no problem here. The only exception would be that you call f during the destruction of the A object, in which case using the captured pointer may be forbidden.

独享拥抱 2025-01-18 19:17:33

这是一个稍微修改过的代码,带有一个极端情况。我在函数中创建一个临时对象,并对其调用 g 并传递给它一个更永久的对象。临时对象消失了,长寿命对象现在有一个闭包来引用寿命结束后的对象。调用f is UB:

#include <iostream>
#include <functional>

struct A {
    void g(A& o, int v) {
        o = A{ .x = v, .f = [this] {
            std::cout << "f" << this->x << ' ' << this << '\n';
        } };
    }
    int x{ 0 };
    std::function<void()> f;
    ~A() {
        std::cout << "dtor" << x <<  ' ' << this << '\n';
    }
};

void test(A& a) {
    A b{ 2 };
    b.g(a, 3);
}

int main() {
    A a{ 1 };
    std::cout << a.x << '\n';
    test(a);
    std::cout << a.x << '\n';
    a.f();   // UB because a.f uses an object after its end of life
}

输出为:

1
dtor3 0135F9C0
dtor2 0135FA30
3
f341072 0135FA30
dtor3 0135FAA8

证明af()的调用在被销毁后尝试使用地址0135FA30处的对象(在该特定运行中) 。

Here is a slightly modified code with a corner case. I create a temporary in a function and call g on it passing it a more permanent object. The temporary vanishes and the long life object now has a closure refering to an object after its end of life. Invoking f is UB:

#include <iostream>
#include <functional>

struct A {
    void g(A& o, int v) {
        o = A{ .x = v, .f = [this] {
            std::cout << "f" << this->x << ' ' << this << '\n';
        } };
    }
    int x{ 0 };
    std::function<void()> f;
    ~A() {
        std::cout << "dtor" << x <<  ' ' << this << '\n';
    }
};

void test(A& a) {
    A b{ 2 };
    b.g(a, 3);
}

int main() {
    A a{ 1 };
    std::cout << a.x << '\n';
    test(a);
    std::cout << a.x << '\n';
    a.f();   // UB because a.f uses an object after its end of life
}

The output is:

1
dtor3 0135F9C0
dtor2 0135FA30
3
f341072 0135FA30
dtor3 0135FAA8

proving that the invocation of a.f() tried to use the object at address 0135FA30 (in that specific run) after it has been destroyed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文