std :: move不创建派生类

时间:2017-04-22 18:32:16

标签: c++ inheritance c++14 move-constructor

我在Base vec中有包含shared_ptr到基类std::vector<std::shared_ptr<Base> >的容器,此容器也有函数add(Base&& base) - 它使用std::move运算符将基础对象添加到vec ,但Derived移动构造函数不调用,只调用base move构造函数。

如何解决:我应该使用dynamic_cast<Derived*>(&base)将其转换为Derived,然后使用std::move

问题:我是否需要尝试强制转换为所有可能的派生类,之后使用std::move

class Base{
public:
    virtual ~Base(){}

    Base(const Base& base) = delete;
    Base(Base&& base){
        std::cout<<"base move\n";
    }
    Base(){}

    virtual void func(){
        std::cout<<"run func from BASE class\n";
    }
};
class Derived: public Base{
public:
    virtual ~Derived() = default;
    Derived(Derived&& derived):Base(std::move(derived)){
        std::cout<<"Derived move\n";
    }
    Derived():Base(){

    }

    virtual void func() override {
        std::cout<<"run func from DERIVED class\n";
    }
};
class Container{
    std::vector<shared_ptr<Base> > vec;
public:
    void add(Base&& base){
        this->vec.push_back(std::make_shared<Base>(std::move(base)));
    }
    void addDerived(Base&& base){
        //TRY ALL POSSIBLE CASTING???
        this->vec.push_back(std::make_shared<Derived>(std::move( *(dynamic_cast<Derived*>(&base)) )));
    }

    void print(){
        for(auto& obj: vec){
            obj->func();
        }
    }
};
int main() {
    std::cout << "Create container and add using function `add`" << std::endl;
    Container container;
    container.add(Derived());
    container.print();

    std::cout << "Create container and add using function `addDerived`" << std::endl;
    Container container_new;
    container_new.addDerived(Derived());
    container_new.print();
}
//Will print
Create container and add using function `add`
base move
run func from BASE class
Create container and add using function `addDerived`
base move
Derived move
run func from DERIVED class

3 个答案:

答案 0 :(得分:1)

这是一个糟糕的界面:

void add(Base&& base){
    this->vec.push_back(std::make_shared<Base>(std::move(base)));
}

您只是始终创建Base。您正在切断基础子对象,这不是一个真正的多态副本。为此,您必须在clone()上添加类似Base方法的内容。

但是将多态性留给用户更简单。您的工作就是提供安全的界面。该界面应该是:

void add(std::shared_ptr<Base> p) {
    vec.push_back(std::move(p));
}

现在,作为用户,我可以为我想要的任何派生类型提供shared_ptr,而不需要您作为界面设计者需要担心它:

container.add(std::make_shared<MySuperCoolDerived>(42)); // cool

答案 1 :(得分:1)

template<class D>
std::enable_if_t< std::is_convertible<std::decay_t<D>*, Base*>::value >
add(D&& d){
  add(std::make_shared<std::decay_t<D>>(std::forward<D>(d)));
}
void add(std::shared_ptr<Base> base){
  this->vec.push_back(std::move(base));
}
template<class D, class...Args>
void emplace(Args&&...args){
  add(std::make_shared<D>(std::forward<Args>(args)...));
}

现在有3种方法可以添加内容。

add(Derives())就像你想要的那样。 add(std::make_shared<Foo>(7))允许您直接注入共享ptrs。

emplace<Derived>(args...)可让您在适当的位置构建它。

添加有一个花哨的SFINAE检查;我跳过它来获得安抚。你可以进行相同的可收敛检查,如果来自Args的D,则可以构造为...而且,当我写入emplace时,我有时会添加first-argument是初始化列表构造函数,因为它修复了完美转发的常见缺陷。

答案 2 :(得分:0)

你需要停止尝试进入对象开始。如果你想拥有一个指向某个基类的指针的容器,但是你不希望用户直接分配你存储的派生类,那么应该由你的插入函数来完成分配。在这种情况下,它应该像典型的标准库emplace函数一样工作:

template<typename T, typename ...Args>
void add(Args && ...args)
{
    static_assert(std::is_base_of<Base, T>::value, "The type must be a base class of `Base`");
    static_assert(std::is_convertible<T&, Base&>::value, "The type must be an *accessible* base class of `Base`");
    this->vec.push_back(std::make_shared<T>(std::forward<Args>(args)...));
}

...

container.add<Derived>();

如果用户有Derived个实例,他们想要转移到由Derived创建的新container,那么他们会container.add<Derived>(std::move(theirDerivedInstance));