当前位置: 首页 > 知识库问答 >
问题:

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

仲孙绍元
2023-03-14

在新的C 20标准中,CpPrerence表示:

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

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!
}

我遇到的问题是更改多态对象的值(不是obj的值,而是多态对象本身的值)时。由于无法重新分配到r值引用,我尝试使用placement new执行以下操作:

#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;
}

我认为我有一个分段错误,因为多态

顺便说一句,请不要告诉我“你的代码很愚蠢”,“使用唯一指针代替”...我知道正常的多态性是如何工作的;我问这个问题是为了加深我对放置new和std::launder的理解:)

共有2个答案

陆栋
2023-03-14

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

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

在您的崩溃示例中,您使用了一个新表达式,因此生命周期不会延长。

益绯辞
2023-03-14

[类别临时]/6.12规定:

临时绑定到新初始化程序中的引用会一直持续到包含新初始化程序的完整表达式完成。

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

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

Placement-new是一个新的初始化器。所以它适用。就像上面一样,base包含一个悬空引用。在那之后如何访问它并不重要;它引用的对象已经被销毁,所以你得到UB。

如果一些代码的读者看不出临时文件的生存期,则不应使用临时文件。只要给它起个名字,你所有的问题就会消失。

 类似资料:
  • 我使用比较器对流进行排序,遇到了一个我不理解的编译器错误。 假设我有以下课程: 我正在创建两个比较器,以按日期对s进行排序,一个按自然顺序,另一个按相反顺序。编译以下代码: 意识到是在上定义的,我想我会尝试以下操作: (对我来说)令人惊讶的是,rev2的代码编译正常,而rev1的代码产生以下错误: 为什么我会收到这些编译器错误?为什么我可以在从构建时有效地规避它们? (如果相关的话,我正在使用Ec

  • 我正在做一些关于JAVA初始化过程的研究。这里有一个很好的参考材料:当一个类在JVM中加载和初始化时 在这个页面上有规则说: 3)如果类初始化是由于访问静态字段而触发的,只有声明静态字段的类被初始化,即使静态字段被类型的子引用,它也不会触发超级类或子类的初始化类,子接口或接口的实现类。 我真的不明白这个想法。如果静态字段被Subclass引用,那么这个字段当然需要创建一个子类对象或由Subclas

  • 为什么即使在Java中使用close()方法,也会出现“Resource leak:”错误? 我把整个代码和注释放在哪里,在哪里得到错误信息,在哪里关闭Scanner方法。 我使用Eclipse,Java13。 这是密码。 甚至当我投入的时候。close()在try块中,仍然显示相同的错误。 即使我把in.close()放在后面,它仍然显示同样的错误。 为什么会这样?

  • 我收到这样的类型错误:- 甚至我正在使用jdk版本:-java版本"1.7.0_55"OpenJDK运行时环境(IcedTea 2.4.7)(7u55-2.4.7-1ubuntu1)OpenJDK 64位服务器VM(构建24.51-b03,混合模式) 请有人帮助我,我应该在我的项目中使用这样的组件。

  • 问题内容: 带有此文件的ImageIO.read(imagePath)给出了CMMException,为什么Java无法处理此看似有效的文件http://www.jthink.net/jaikoz/scratch/front.jpg 问题答案: 我想我掌握了您的问题。我检查了您链接的图像(http://www.jthink.net/jaikoz/scratch/front.jpg)。其归因于Exi

  • 问题内容: 我有一个带有IF-ELSE块的SQL代码。IF部分中的代码不应该到达,但是在执行SQL时仍然出现错误。在我首先测试链接服务器的代码中,当它失败时,将其设置为1,并且该块应执行并避免需要查询链接服务器的块中的代码,但是出现此错误: 我正在SSMS 2012中运行查询。为什么会发生此错误? -下列插入SQL语句导致错误 问题答案: 在执行之前,它仍然会解析并绑定所有内容。它没有绑定到这里。