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

use*_*498 12 c++ reference placement-new c++11

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

但是,我以某种方式尝试以下代码,它恰好适用于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
Run Code Online (Sandbox Code Playgroud)

上面的代码适用于g ++和clang ++,但它是不是很好的C++?谢谢.

Pas*_* By 23

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

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
Run Code Online (Sandbox Code Playgroud)

在这种情况下,修复很简单

auto p2 = new (&p) std::pair<std::string&, std::string&> {i, j};
p2->first = "1";
p2->second = "2";
Run Code Online (Sandbox Code Playgroud)

另一种解决方案是C++ 17功能 std::launder

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

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

  • 不得不在`std :: launder`标准链接上进行双击.被[[[nodiscard]]`属性抛弃了.你的最后一个片段是完全有效的C++ 17(和20),但我花了一点时间来解析. (3认同)
  • @YSC - 一眼就能看出我最后的片段丢弃了结果(虽然它没有,显然已经使用了).所以我有一个轻微的WTF时刻.然后我回忆起通常是在点Passer By,我重新解析了片段 (2认同)

Sto*_*ica 20

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

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

但是你应该知道你没有重新引用任何引用.你杀了一个持有引用的对象,并在同一个地方创建一个新的.从概念上讲,你有两个"旧"引用,现在有两个"新"引用.

你的代码可以很好,因为该对是一个包含一对引用的简单结构,如果该对保持任何简单的可破坏类型,它将是有效的.但是,如果任何对元素的d'tor不是微不足道的,那么你将有未定义的行为.因为析构函数不会作为放置新的一部分执行.

Passer注意到的问题是,你不能p用来引用"新对象",因为它包含引用.这将是UB的原因.

它是好的C++?

这是值得商榷的.这当然不是人们期望经常看到的.

  • @StoryTeller只是仔细检查了事实,对不起即时删除的评论.在那种情况下,它会[只是UB](https://timsong-cpp.github.io/cppwp/n3337/basic.life#7.3) (2认同)