【问题标题】:Placement new on this pointer [duplicate]在此指针上放置新 [重复]
【发布时间】:2019-04-13 14:19:22
【问题描述】:

2 个打印语句打印不同的数字。据我所知,我在这里没有做任何狡猾的const_cast,所以我不确定我可能犯了什么UB。

  1. 这段代码格式正确吗?

  2. 编译器能否依赖 A::numconst 的事实,因此允许打印相同的数字?

代码:

struct A
{
    const int num = 100;

    A() {}    

    A(int in) : num{in} {}

    void call()
    {
        new (this) A{69};
    }
};

int main()
{
    A a;    
    std::cout << a.num << '\n';
    a.call();
    std::cout << a.num << '\n';
}

【问题讨论】:

  • 我要冒昧地说,对于已经包含现有类实例的内存,placement new 几乎可以肯定是未定义的行为。您可能会侥幸逃脱(尽管我假设编译器会警告您),因为不涉及内存分配或虚函数,但这似乎是最狡猾的想法。
  • [basic.life] "在具有静态、线程或自动存储持续时间的 const 完整对象所占用的存储空间内,或在这样的存储空间内创建一个新对象用于在其生命周期结束之前占用的 const 对象,会导致未定义的行为。"
  • @ShadowRanger 信不信由你,您可以重复使用活动对象的存储空间! [basic.life] "程序可以通过重用对象占用的存储空间或通过显式调用具有非平凡析构函数的对象的析构函数来结束对象的生命周期。对于具有非平凡析构函数的类类型,程序不需要在对象占用的存储被重用或释放之前显式调用析构函数..."
  • @RaymondChen 这是真的吗,即使只有成员是 const? “完整对象”在 [intro.object] 中定义为“不是任何其他对象的子对象的对象称为完整对象。”
  • @RaymondChen a 不是 const,所以新的展示位置不是 UB。

标签: c++ constants language-lawyer undefined-behavior placement-new


【解决方案1】:

不,您的代码有 UB。删除num 上的const,您将不再获得任何UB。

问题在于标准提供了const 对象不会改变的保证。但如果你重用相同的存储,那么你可以通过某种方式“修改”const 对象。

[basic.life]p8 通过说对象的旧名称仅在某些条件下引用新对象来明确禁止这一点。其中之一是您的班级没有任何 const 成员。因此,通过扩展,您的第二个 a.num 是 UB,因为 a 指的是旧的破坏对象。

但是,有两种方法可以避免这种 UB。首先,您可以存储指向新对象的指针:

struct A *new_ptr;
struct A {
  // [...]
  void call() {
      new_ptr = new (this) A{69};
  }
};

int main()
{
    A a;    
    std::cout << a.num << '\n';
    a.call();
    std::cout << new_ptr->num << '\n'; // ok
}

或者使用std::launder:

std::cout << std::launder(&a)->num << '\n'; // second access

【讨论】:

  • 是的。但请注意,根据指针是 POD(或普通类型)的理论(由标准假设),使用 std::launder 本质上是多余的,因为给定类型和给定字节的所有普通类型明智的表示必须具有相同的意图价值,而不仅仅是相同的数值。或者琐碎的类型真的是一个毫无价值的概念。
猜你喜欢
  • 2018-07-20
  • 1970-01-01
  • 2017-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-08
  • 2019-08-03
  • 2015-12-20
相关资源
最近更新 更多