在新的C++20标准中,副院长说
临时绑定到使用直接初始化语法(括号)初始化的聚合的引用元素中的引用,而不是列表初始化语法(大括号),直到包含初始化器的完整表达式结束为止。示例: 结构A{int& r;};A a1{7};// OK,生存期扩展为A a2(7);//格式良好的引用,但存在悬空引用。
注:我使用GCC,因为这是唯一的编译器,支持这个功能。在阅读了这篇文章,并且知道引用是多态的之后,我决定创建一个简单的类:
template <typename Base>
struct Polymorphic {
Base&& obj;
};因此,下面的代码可以工作:
struct Base {
inline virtual void print() const {
std::cout << "Base !\n";
}
};
struct Derived: public Base {
inline virtual void print() const {
std::cout << "Derived !" << x << "\n";
}
Derived() : x(5) {}
int x;
};
int main() {
Polymorphic<Base> base{Base()};
base.obj.print(); // Base !
Polymorphic<Base> base2{Derived()};
base2.obj.print(); // Derived 5!
}我遇到的问题是当更改多态对象的值时(不是obj的值,而是多态对象本身的值)。由于无法重新分配r值引用,所以我尝试使用placement执行以下操作:
#include <new>
int main() {
Polymorphic<Base> base{Base()};
new (&base) Polymorphic<Base>{Derived()};
std::launder(&base)-> obj.print(); // Segmentation fault... Why is that?
return 0;
}我相信我有一个分段错误,因为Polymorphic<Base>有一个引用子对象。然而,我正在使用std::launder --这不是应该让它工作吗?或者这是GCC的问题?如果std::launder不能工作,我如何告诉编译器不要缓存引用?
另外,请不要告诉我“您的代码很愚蠢”,“使用唯一的指针”.我知道正常的多态是如何工作的;我问这个问题是为了加深我对安置新的和std::launder :)的理解。)
发布于 2021-02-12 17:12:16
临时绑定到新初始化器中的引用,直到包含新初始化器的完整表达式完成为止。
对于如何初始化引用并不挑剔;这适用于所有此类引用被初始化的方式。事实上,甚至有一个例子:
结构S{ int mi;const std::pair& mp;};S{ 1,{2,3 };S* p=新的S{ 1,{2,3 };//创建悬挂引用
安置-新是一个新的-初始化。所以它适用。和上面的一样,base包含一个悬空引用。在这一点之后,如何访问它并不重要;它引用的对象已经被销毁,所以您得到了UB。
如果某些代码的读者不清楚临时的生存期,则不应该使用临时的。只要给它起个名字,你的问题就都解决了。
发布于 2021-02-12 17:57:05
看看您在提供的链接中引用的项目上方的要点:
一个临时绑定到在新表达式中使用的初始化器中的引用的临时绑定,直到包含该新表达式的完整表达式结束为止,而不像初始化的对象那样长。如果初始化的对象比完整表达式的生存期更长,则其引用成员将成为一个悬空引用。
在您的崩溃示例中,您使用的是一个新表达式,因此不延长生存期。
https://stackoverflow.com/questions/66175996
复制相似问题