Можно ли сбросить ссылку на другое значение в 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++?
Это спорно. Конечно, это не то, что можно было бы увидеть часто.