首页 > 解决方案 > 从引用创建一个指向基类的智能指针

问题描述

我有一个容器,它存储一个智能指针到基类的向量,我想通过一种方法填充它,而不需要我的用户也创建那个智能指针:

class Base {
    // ...
};
class Derived: public Base {
    // ... 
};

class Collection {
private:
    vector<unique_ptr<Base>> pointers;

public:
    void add(Base&& value) // #1
    {
        pointers.push_back(????);  
    }

    void add<typename T>(T&& value) // #2
    {
        pointers.push_back(????);
    }
};

int main() {
    Collection collection;
    collection.add(Derived("Data"));  // #3
}

如果有的话,正确的方法是什么?很明显,我可以使用make_unique和 emplacement,除了我担心派生的内容不会被正确移动。

可能我在 Rust 土地上花了太多时间,这种移动非常普遍,所以如果我在这里离基地很远,请告诉我。理想情况下,该接口看起来像我的 #3 点,可以使用派生类型的文字调用函数,而无需任何与分配或任何其他样板相关的额外样板。如果解决方案最终是Collection::add通用的,我会找到的。

标签: c++

解决方案


您可能应该坚持使用模板,是的。然后你得到

class Collection {
    std::vector<std::unique_ptr<Base>> pointers;

public:
    template<typename T>
    void add(T &&value) {
        pointers.emplace_back(std::make_unique<std::remove_reference_t<T>>(std::forward<T>(value)));
    }
};

int main() {
    Collection c;
    Derived d;
    c.add(d); // works with lvalues (copying)
    c.add(std::move(d)); // or rvalues (moving)
    Base b;
    c.add(b);
    c.add(std::move(b));
}

然而,提供一个“emplace”可能更有用,它用任意参数构造一个对象(所有标准容器都提供)

class Collection {
    std::vector<std::unique_ptr<Base>> pointers;

public:
    template<typename T, typename... Ts>
    void emplace(Ts&&... args) {
        pointers.emplace_back(std::make_unique<T>(std::forward<Ts>(args)...));
    }
    template<typename T> // still useful for conciseness (don't have to specify T)
    void add(T &&value) {
        this->emplace<std::remove_reference_t<T>>(std::forward<T>(value));
    }
};

所以你可以进一步做

int main() {
    Collection c;
    Derived d;
    c.add(d); // works with lvalues
    c.add(std::move(d)); // or rvalues
    c.emplace<Derived>(); // or can give arguments directly (assuming a default constructor, in this case)
    Base b;
    c.add(b);
    c.add(std::move(b));
    c.emplace<Base>();
}

关于 Godbolt 的完整示例。


推荐阅读