以下代码在 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);
}
引用不应该被绑定两次,对吧?
以下代码在 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);
}
引用不应该被绑定两次,对吧?
这是完全无效的。
[basic.life]/1,强调我的:
类型对象的生命周期在以下情况下
T
结束:
- 如果
T
是具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或者- 对象占用的存储空间被重用或释放。
放置 new 重用存储,结束由 表示的对象的生命周期f
。
[基本生活]/7:
如果在对象的生命周期结束之后,在对象占用的存储空间被重用或释放之前,在原始对象占用的存储位置创建一个新对象,一个指向原始对象的指针,一个指向原始对象的引用引用原始对象,或者原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,可用于操作新对象,如果:
- 新对象的存储恰好覆盖了原始对象占用的存储位置,并且
- 新对象与原始对象的类型相同(忽略顶级 cv 限定符),并且
- 原始对象的类型不是 const 限定的,并且,如果是类类型,则不包含任何类型为 const 限定或引用类型的非静态数据成员,并且
- 原始对象是类型最派生的对象(1.8),
T
新对象是类型最派生的对象T
(也就是说,它们不是基类子对象)。
由于不满足第三个要点,在调用 之后Bar
,f
不会引用由放置创建的对象new
,而是指向先前存在的不再存在的对象,并且尝试使用它会导致未定义的行为。
这可能是合法的,但它的风格非常糟糕。Placement new 的参数是 void*,所以你告诉 C++ 将 f 的地址重新解释为 void*,然后使用它作为位置来构造新的东西 - 覆盖原来的 f。
基本上,不要那样做。