是否可以在 C++ 中重置对另一个值的引用?

Is it possible to reset reference to another value in 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++ 好吗?谢谢

My question is, is the following a valid (behavior-defined) C++?

It could be。这里的关键是那对。您结束了 pair 对象的生命周期,并开始了同一存储中另一个 pair 对象的生命周期(placement new 完成了这两件事)。

但是您应该知道您没有重新绑定任何引用。您杀死了一个包含引用的对象,并在同一位置创建了一个新对象。从概念上讲,您有两个 "old" 引用,现在有两个 "new" 引用。

您的代码可能没问题,因为该对是包含一对引用的简单结构,并且如果该对包含任何可平凡破坏的类型,则它是有效的。但是,如果任何 pair 元素的 d'tor 都不是微不足道的,那么您将有未定义的行为。因为析构函数不会作为 placement new 的一部分执行。

的问题是您不能使用 p 来引用 "new object",因为它包含引用。这就是 UB 的原因。

is it good C++?

这是有争议的。这当然不是人们期望经常看到的东西。

该代码段具有未定义的行为,但几乎没有。从概念上讲,您销毁了旧引用并创建了新引用,即使您重新使用了内存,也没有重新绑定引用。这部分完全没问题。

The catch是如果重用的class包含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";

这些规则presumably 使编译器能够围绕引用和 const 成员进行更多优化。