Является ли следующий код допустимым в 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
,
Если после окончания срока службы объекта и до хранения
занятый объект используется повторно или освобождается, новый объект
созданный в месте хранения, которое занимал исходный объект,
указатель, указывающий на исходный объект, ссылка, на которую ссылается
к исходному объекту, или имя исходного объекта будет
автоматически ссылаться на новый объект и, по истечении времени жизни
новый объект запущен, может использоваться для манипулирования новым объектом, если:
- хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и
- новый объект того же типа, что и исходный объект (без учета cv-квалификаторов верхнего уровня), и
- тип исходного объекта не является константно-квалифицированным, и, если тип класса, не содержит какого-либо нестатического члена данных, тип которого
квалифицированный const или ссылочный тип, а также- исходный объект был наиболее производным объектом (1.8) типа
T
и новый объект является наиболее производным объектом типаT
(то есть они
необъекты базового класса).
Поскольку третий пункт пули не выполняется, после звонка Bar
, f
не относится к объекту, созданному в результате размещения new
, но для ранее не существующего объекта, и попытка использовать его приводит к неопределенному поведению.
Это может быть законно, но это невероятно плохой стиль. Аргументом для размещения new является void *, поэтому вы говорите C ++ переинтерпретировать_качественный адрес f как void *, а затем используете его как местоположение для создания чего-то нового — перезаписывая исходный f.
По сути, не делай этого.