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

时间:2018-04-26 09:43:27

标签: c++ c++11 reference placement-new

我知道通常在初始化之后重置参考是不可能的。

但是,我试着尝试以下代码,它恰好适用于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 ++?感谢。

2 个答案:

答案 0 :(得分:23)

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

The catch如果重用的类包含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成员进行更多优化。

答案 1 :(得分:20)

  

我的问题是,以下是有效的(行为定义的)C ++吗?

It could be。这里的关键是那对。您结束了对对象的生命周期,并在同一存储中启动了另一对对象的生命周期(placement new会同时执行这两项操作)。

但是你应该知道你并没有重新引用任何引用。你杀了一个持有引用的对象,并在同一个地方创建一个新的。从概念上讲,你有两个&#34; old&#34;参考文献,现在两个&#34; new&#34;的。

您的代码可能没问题,因为该对是一个包含一对引用的简单结构,如果该对保持任何简单的可破坏类型,它将是有效的。但是,如果任何对元素的数据都不是微不足道的,那么你将会有未定义的行为。因为析构函数不会作为放置新内容的一部分执行。

问题为Passer By noted,您无法使用p来引用&#34;新对象&#34;,因为它包含引用。这将是UB的原因。

  

是不是很好的C ++?

这是值得商榷的。这当然不是人们经常会看到的。