首页 > 解决方案 > 多态 r 值引用?

问题描述

如果我想在一个类中存储一个多态对象,我的第一个想法是这样的:

struct Base {
    virtual void print() const {
        std::cout << "Base !";
    }
};

struct Derived: public Base {
    void print() const {
        std::cout << "Derived !";
    }
};

struct PolymorphicWrapper {
    std::unique_ptr<Base> base;
};

问题std::unique_ptr<Base>在于它会导致复杂的所有权问题和指针语义。虽然引用/原始指针可以具有多态行为,但它们不能绑定到 r 值。

但是我刚刚发现 r 值引用是多态的!

例如:

Base && base = Derived();
base.print(); // Derived !

Base&& base2 = Base();
base2.print(); // Base !

我什至可以在一个类中存储一个 r 值引用!

struct PolymorphicWrapper {
     Base&& base;
     PolymorphicWrapper(auto iBase) : base(std::move(iBase)) {} // auto to prevent object slicing
};

然而,这似乎好得令人难以置信。我在这里错过了什么吗?这是未定义行为的示例吗?

标签: c++polymorphismrvalue-reference

解决方案


std::move创建对现有左值的右值引用,这将其明确标记为准备好从中移动。您可以从中初始化另一个右值引用,但是由于两个原因,您不在特殊的生命周期扩展案例中:

  • 它需要一个实际的右值(即一个刚刚在表达式中构造并且从未通过引用传递的对象);

  • 更重要的是,它根本不适用于引用成员。

注意:我有理由确定我做对了,但我似乎找不到标准报价,可能是因为我在聚合初始化和临时实现措辞中迷失了方向。欢迎语言律师。

因此,您只是存储对构造函数参数的引用,该参数在所述构造函数的末尾死亡,并使引用悬空。

请注意,在将任意类型擦除的对象存储为成员时,在一般情况下您无法避免动态分配,如果仅仅是因为动态类型的大小未知且无界。std::unique_ptr是这种情况下的首选,但如果需要,您也可以设计自己的带有小型对象存储的类型擦除容器。


推荐阅读