以下代码在C++中是否合法?使用placement new来更新引用成员?
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);
}
引用不应该被绑定两次,对吧?
以下代码在C++中是否合法?使用placement new来更新引用成员?
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);
}
引用不应该被绑定两次,对吧?
这是完全无效的。
[basic.life]/1,重点煤矿:
T
类型的对象的生存期结束时:
- 如果
T
是具有一个非平凡的析构函数的类类型(12.4),析构函数调用开始,或者- 对象占用的存储空间被重用或释放。
放置新重用存储,结束由f
表示的对象的生存期。
[basic.life]/7:
如果一个对象的生命周期结束之后和其占据的对象被重新使用或释放的存储 之前,一个新的对象是在创建 原始对象占用的存储位置, 指向原始对象的指针,引用 至原始对象,或者原始对象的名称将自动引用新对象,并且一旦该对象的生命周期 新对象已启动,可用于操纵新对象,如果:
- 存储为新对象恰好覆盖其原始对象所占据的存储位置,和
- 新的对象是相同的类型与原始对象的(忽略顶层cv修饰符),和
- 原始对象的类型不是常量限定,并且,如果一个类型,不包含任何非静态数据成员,其类型是 常量限定或引用类型,和
- 原始对象是类型为
T
的最衍生对象(1.8),t他新对象是类型为T
的最衍生对象(即,它们不是基类子对象的 )。
由于第三点不服气,要Bar
一个电话后,f
并不是指由放置new
创建的对象,但对不再视为生命的物体之前在那里,并试图使用它会导致未定义的行为。
我不明白。在放置新指令中:新对象的创建是否意味着对象占用的存储的重新使用?在这种情况下,在放置新对象之后,对于我来说,条件:“在对象占用的存储空间被重用或释放之前”未得到满足,因为在存储被重用之后将来会使用f。我不明白这些规则如何组合在一起。 – Pumkko
这可能是合法的,但它的风格令人难以置信。放置new的参数是一个void *,所以你告诉C++将f的地址重新解释为一个void *,然后使用它作为构造新东西的位置 - 覆盖原始f。
基本上,不要这样做。
为什么不呢?这是完全类型安全的。 – Davislor
C++中几乎没有什么是“完全类型安全的” - C++并不真正知道这个词的含义。如果Foo使用复杂类型实例化会怎么样?甚至是一个字符串? reinterpret_cast覆盖对象的构造内容。 – cliffordheath
好的,在几乎所有现实世界的情况下,您都希望赋值运算符在这里,这将正确地破坏复杂类型。也就是说,这对于普通的旧数据或其析构函数调用的对象来说很好。换句话说,它是C++,让你再次拍摄自己的脚。 – Davislor
你没有*绑定参考'v_'两次,你用'Bar'中'Foo(a)'初始化的对象的数据覆盖它。 –
“引用不应被绑定两次,对吗?” - 不,但是这与你的其他问题有什么关系? –
@JoachimPileborg谢谢,现在我回头看,最后一句话没有意义。引用不能被绑定两次,这不像我们可以做任何事情来重新绑定引用。 –