是否可以在 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 成员进行更多优化。
我知道一般情况下不可能在初始化后重置引用。
但是,我以某种方式尝试了以下代码,它恰好适用于 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 成员进行更多优化。