You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同一内存地址存在两个同类型对象时std::launder的指针指向问题咨询

同一内存地址存在两个同类型对象时std::launder的指针指向问题咨询

我最近在研究std::launder的时候,碰到了一个标准里似乎没明确覆盖的场景,想来和大家探讨一下。

先给大家回顾一下cppreference对std::launder(T *p)的定义:

形式上,假设指针p代表内存中某个字节的地址A:

  • 有一个对象x位于地址A处
  • x正处于其生命周期内
  • x的类型与T相同(忽略所有层级的cv限定符)
  • 通过返回结果可访问的每个字节,都能通过p访问到
    ...
    那么std::launder(p)会返回一个指向对象x的T*类型值。否则行为未定义。

我对这段定义的理解是:如果p指向的内存位置上存在一个T类型对象,那么std::launder(p)就会返回指向该T对象的指针。之前看到的例子大多是讲std::launder用来处理“一个对象生命周期结束,另一个对象在同一块内存开始生命周期”的场景,用来告诉编译器这个变化。但按我的理解,它的用法其实不止于此。

比如下面这个例子:

struct S {
    int i;
    S(int i, int j) : i(i), j(j) {}
private:
    int j;
};

int main() {
    S s(10, 20);
    int *pi = reinterpret_cast<int*>(&s);
    int *pi_good = std::launder(pi);
}

这里pi是一个int*,它指向的内存位置上确实存在一个int对象,但指针不只是指向内存地址,更是指向对象。因为S和int不是指针可互转的类型,所以pi实际上仍然指向的是S对象,直接解引用pi是未定义行为。而std::launder在这里就派上用场了,它能给我们一个真正指向那个int对象的指针。

这就扩展了std::launder的适用场景——不止是对象替换的情况,甚至在同一内存地址同时存在两个不同对象的时候,我们也能用它在指向不同对象的指针之间切换(反过来也一样)。(这也意味着std::launder(reinterpret_cast<T*>(std::rand()))在某些情况下可能是合法的。)

现在我遇到的核心问题是:如果同一内存地址上同时存在两个同类型的对象,对指向该地址的指针调用std::launder后,返回的指针会指向哪一个对象?

比如下面这个例子:

#include <new>

struct Holder {
    // 用unsigned char提供存储
    unsigned char storage[4];
    // 让它成为非平凡类型
    ~Holder() {};

    template <typename T>
    void hold() {
        ::new(static_cast<void *>(storage)) T;
    }
};

int main() {
    Holder h;
    h.hold<Holder>();
    Holder *ph = &h;
    [[maybe_unused]] Holder *ph_laundered = std::launder(ph);
}

这里Holder对象h和它内部存储里构造的另一个Holder对象,同时存在于同一个内存地址上,而且类型完全相同。那么调用std::launder(ph)后,返回的指针是指向原来的h,还是指向新构造的那个Holder对象呢?

*补充一下之前例子里的细节:定义里最后一个要求是,S*不能访问任何int*无法访问的字节。我们可以把S改成如下定义,既保持S和int不可指针互转,又满足这个要求:

class S {
private:
    int i;
};

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 13:09:30