C++ 为什么在引用类型上使用placement new会导致分段错误,即使使用std::launder也是如此?

C++ 为什么在引用类型上使用placement new会导致分段错误,即使使用std::launder也是如此?,c++,c++20,rvalue-reference,placement-new,C++,C++20,Rvalue Reference,Placement New,在新的C++20标准中: 使用直接初始化语法(括号)而不是列表初始化语法(大括号)初始化的聚合的引用元素中的引用的临时绑定一直存在,直到包含初始值设定项的完整表达式结束。例如: struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference 注意:我使用的是GCC,因为这是唯一支持此功能的编译器。 读完本文,知道引用是多态

在新的C++20标准中:

使用直接初始化语法(括号)而不是列表初始化语法(大括号)初始化的聚合的引用元素中的引用的临时绑定一直存在,直到包含初始值设定项的完整表达式结束。例如:

struct A {
   int&& r;
};
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference
注意:我使用的是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!
}
struct Base{
内联虚空打印()常量{
标准::cout规定:

在包含新初始值设定项的完整表达式完成之前,与新初始值设定项中引用的临时绑定将一直存在

对于如何初始化引用并不挑剔;这适用于所有初始化此类引用的方式。事实上,甚至有一个示例:

struct S{int mi;const std::pair&mp;};
sa{1,{2,3};
S*p=news{1,{2,3};//创建悬挂引用
Placement new是一个新的初始值设定项。因此它适用。正如上面所述,
base
包含一个悬空引用。在该点之后,您如何访问它并不重要;它引用的对象已被销毁,因此您将获得UB


如果一些代码的读者看不出临时文件的使用期限,那么就不应该使用临时文件。只要给它起个名字,你的问题就会迎刃而解。

看看你在提供的链接中引用的项目符号上方的项目符号:

与新表达式中使用的初始值设定项中的引用的临时绑定存在,直到包含该新表达式的完整表达式结束,而不是与初始化对象的时间一样长。如果初始化对象比完整表达式寿命长,则其引用成员变为挂起引用


在你的崩溃例子中,你使用的是一个新的表达式,所以生命周期没有被延长。

如果你要引用一些文本,你应该清楚你引用的是什么。我假设你引用了C++标准,而不是引用文档。特别是因为这就是你所说的。Cppreference不是“C++ 20标准”。。我认为
std::launder
与您看到的分段错误之间没有联系。
std::launder
据我所知,有助于防止编译器对指针做出一些假设,主要是关于值的常量和动态类型。大多数开发人员甚至不应该知道这一点,这是超级规范科学。这看起来像一个普通的悬空指针/未初始化指针。@MooingDuck:它实际上是合法的。当然,如果这个东西是锁,或者编译器认为它有静态生存期,然后破坏了一个不是这个东西的东西,或者,或者,或者…那么你会得到bug或者UB。但是通常是允许的。@davidbak:My“good get UB”在它之前有条件是有原因的。:)我将引用包含正确强调引号的引用。我的意思是这是C++:仅仅因为你可以这样做并不意味着这样做是安全的。你可以自由地重新使用存储自动(堆栈)为一个全新的对象分配对象-但当编译器试图破坏原始对象时,现在您得到UB时,不要感到惊讶。但是您可以自由地,例如
newvector
,然后用其他对象完全替换它(当然,内存泄漏,但这是允许的)@GManNickG-Huh,我的理解是,如果你不称一个非平凡的析构函数为“它有[可观察到的]副作用”,那么它就是UB。但我认为这意味着它不合法-除非条件得到满足!-我相信这是标准中经常出现的东西。所以我会说“它实际上不合法,除非…”.你也这么认为吗?所以没有办法给我的多态对象重新赋值了?@Cyrus:这与“重新赋值”无关,而与“重新赋值到临时对象”有关停止依赖于临时生命周期扩展的深奥规则,所有的问题都会消失。学习依赖于这些规则的C++代码只意味着你编写C++代码,要求读者有详细的标准ARKANA知识,看看它是否有效。你不应该这样做;先写清楚代码。etime?@Cyrus:不。停止依赖临时生存期扩展。
new(&base)多态性{*new-Derived()};
(根本不使用临时)是我能想到的唯一方法,但这还有其他问题。
#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;
}
struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };
S* p = new S{ 1, {2,3} };       // creates dangling reference