Можно ли сбросить ссылку на другое значение в C++?

Я знаю, что вообще невозможно сбросить ссылку после ее инициализации.

Тем не менее, я как-то пробую следующий код, и он работает как на clang++, так и на g++.

Мой вопрос заключается в следующем: действительный (определяемый поведением) C++?

std::string x = "x";
std::string y = "y";
std::string i = "i";
std::string j = "j";

// now references to x, y
std::pair<std::string &, std::string &> p { x, y };
p.first = "1"; //changes x
p.second = "2"; //changes y
// now references to i, j
new (&p) std::pair<std::string &, std::string &> {i, j};
p.first = "1"; //changes i
p.second = "2"; //changes j

Вышеприведенный код работает на g++ и clang++, но хорошо ли C++? Благодарю.

Ответ 1

У фрагмента есть неопределенное поведение, но едва ли. Понятно, что вы уничтожили старые ссылки и создали новые, вы не перепроверяли ссылку, даже если вы повторно использовали память. Эта часть полностью прекрасна.

Ловушка заключается в том, что если повторно используемый класс содержит 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";

Другим решением является функция С++ 17 std::launder

new (&p) std::pair<std::string &, std::string &> {i, j};
std::launder(&p)->first = "1";
std::launder(&p)->second = "2";

Эти правила, по- видимому, позволяют компилятору сделать больше оптимизаций вокруг ссылок и константных членов.

Ответ 2

Мой вопрос заключается в следующем: действительный (определяемый поведением) C++?

Это может быть. Ключом здесь является пара. Вы закончили время жизни объекта-пары и запустили время жизни другого парного объекта в том же хранилище (новое место размещения делает обе эти вещи).

Но вы должны знать, что не переписываете какие-либо ссылки. Вы убиваете объект, содержащий ссылки, и создаете новое в том же месте. По идее, у вас были две "старые" ссылки, а теперь две "новые".

Ваш код может быть точным, потому что пара представляет собой простую структуру, содержащую пару ссылок, и это было бы справедливо, если бы пара содержала любые тривиально разрушаемые типы. Если, однако, любой элемент пары не является тривиальным, у вас будет неопределенное поведение. Поскольку деструкторы не будут выполняться как часть нового места размещения.

Проблема, отмеченная как Passer By, заключается в том, что вы не можете использовать p для обозначения "нового объекта", поскольку он содержит ссылки. Это было бы причиной UB.

это хорошо C++?

Это спорно. Конечно, это не то, что можно было бы увидеть часто.