【问题标题】:Using placement new to update a reference member?使用placement new 来更新参考成员?
【发布时间】:2015-11-04 06:06:46
【问题描述】:

以下代码在 C++ 中是否合法?

template<typename T>
class Foo {
public:
    Foo(T& v) : v_(v) {}

private:
    T& v_;
};

int a = 10;
Foo<int> f(a);

void Bar(int& a) {
    new (&f)Foo<int>(a);
}

引用不应该被绑定两次,对吧?

【问题讨论】:

  • 你没有绑定引用v_两次,你用@中Foo&lt;int&gt;(a)初始化的对象中的数据覆盖它987654324@.
  • "引用不应该被绑定两次,对吧?" – 不,但这与您的其余问题有什么关系?
  • @JoachimPileborg 谢谢,现在我回头看,最后一句话没有意义。引用不能被绑定两次,我们不能做任何事情来重新绑定引用。
  • 当前的代码是合法的,只要之后你不访问原始对象(例如通过f)。

标签: c++ reference object-lifetime placement-new reference-binding


【解决方案1】:

这是完全无效的。

[basic.life]/1,强调我的:

T 类型对象的生命周期在以下时间结束:

  • 如果 T 是具有非平凡析构函数 (12.4) 的类类型,则析构函数调用开始,或者
  • 对象占用的存储空间被重用或释放。

placement new 重用存储,结束由f 表示的对象的生命周期。

[basic.life]/7:

如果,在对象的生命周期结束之后并且在存储之前 被占用的对象被重用或释放,一个新的对象是 在原始对象占用的存储位置创建,a 指向原始对象的指针,引用的引用 到原始对象,或者原始对象的名称将 自动引用新对象,并且一旦生命周期 新对象已启动,可用于操作新对象,如果:

  • 新对象的存储空间正好覆盖了原始对象占用的存储位置,并且
  • 新对象与原始对象的类型相同(忽略顶级 cv 限定符),并且
  • 原始对象的类型不是 const 限定的,并且,如果是类类型,则不包含任何类型为 const 限定或引用类型,以及
  • 原始对象是T 类型的最衍生对象(1.8),而新对象是T 类型的最衍生对象(也就是说,它们是 不是基类子对象)。

由于不满足第三个要点,在调用Bar 之后,f 不是指由放置new 创建的对象,而是指先前存在的不再存在的对象,并且尝试使用它会导致未定义的行为。

另请参阅CWG1776P0137R0

【讨论】:

  • 我有些不明白。在放置新指令中:新对象的创建不就意味着对象所占用的存储空间的再利用吗?在这种情况下,在放置 new 之后,对我来说,条件:“在重用或释放对象占用的存储之前”不满足,因为未来使用 f 发生在重用存储之后。我不明白这些规则是如何组合在一起的。
【解决方案2】:

这可能是合法的,但它的风格非常糟糕。 Placement new 的参数是 void*,所以你告诉 C++ 将 f 的地址重新解释为 void*,然后使用它作为位置来构造新的东西 - 覆盖原来的 f。

基本上,不要那样做。

【讨论】:

  • 为什么不呢?它是完全类型安全的。
  • C++ 中几乎没有什么是“完全类型安全的”——C++ 并不真正了解这个词的含义。如果 Foo 是使用复杂类型实例化的呢?甚至是一个字符串? reinterpret_cast 覆盖对象的构造内容。
  • 好吧,在几乎所有现实世界的情况下,您都需要这里的赋值运算符,它将正确地破坏复杂类型。也就是说,这对于普通的旧数据或已调用其析构函数的对象来说很好。换句话说,是 C++ 让你又一次朝自己的脚开枪。
  • 即使这样,赋值运算符也只有在你正确定义它的情况下才可以(或者它是 POD 而你不需要) - 默认只是覆盖对象。
  • 任何东西只有在正确定义的情况下才会起作用。是的,默认赋值运算符可能会在一些写得不好的类上失败。
猜你喜欢
  • 2021-08-22
  • 1970-01-01
  • 2021-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2018-02-20
  • 2013-12-14
相关资源
最近更新 更多