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

Overwriting object with new object of same type and using closure using this

在下面的代码中,一个对象被相同类型的新对象覆盖,其中 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();
}

您实际上并没有替换任何对象。您只是将另一个对象 分配 到当前对象。 o = 只是调用隐式复制赋值运算符,它将 copy-assign 来自临时 A 的各个成员,这些临时 AA{...}.

的赋值表达式中构造

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

std::function 将始终保留一份 lambda 的副本,该副本引用调用 g 的原始对象,因为这是它的父对象,它不能比它长。

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

这里是一个稍微修改过的代码,带有一个特殊情况。我在一个函数中创建了一个临时对象并在其上调用 g 并向其传递一个更 永久 的对象。临时对象消失了,长寿命对象现在有一个闭包,指向生命结束后的对象。调用 f 是 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

证明 a.f() 的调用试图在地址 0135FA30(在那个特定的 运行 中)被销毁后使用该对象。