我在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
答案 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));
。