Фрагмент имеет неопределенное поведение, но это не так. Концептуально, вы уничтожили старые ссылки и создали новые, вы не перепечатали ссылку, даже если вы повторно использовали память. Эта часть полностью в порядке.
Подвох заключается в том, что если повторно используемый класс содержит const
или ссылочные члены, то исходное имя переменной нельзя использовать для ссылки на новый объект
new (&p) std::pair<std::string &, std::string &> {i, j};
// p does not refer to the newly constructed object
p.first = "1"; // UB
p.second = "2"; // UB
Исправление простое, в данном случае
auto p2 = new (&p) std::pair<std::string&, std::string&> {i, j};
p2->first = "1";
p2->second = "2";
Другим решением является функция C ++ 17 std::launder
new (&p) std::pair<std::string &, std::string &> {i, j};
std::launder(&p)->first = "1";
std::launder(&p)->second = "2";
Эти правила предположительно позволяют компилятору оптимизировать работу со ссылками и константными членами.