2017-04-22 11 views
-2

파생 생성하지 않는,이 컨테이너는 기능 add(Base&& base)이있다 - 어떤 std::move 연산자를 사용하여 VEC에 기본 오브젝트를 추가,하지만 파생 이동 생성자는하지 않습니다 호출, 기본 이동 생성자 만 호출됩니다.표준 : 이동이 나는 <code>std::vector<std::shared_ptr<Base> ></code> VEC의 기본 클래스 <code>Base</code>에있는 shared_ptr을 포함 컨테이너가 클래스

수정 방법 : dynamic_cast<Derived*>(&base)을 사용하여 Derived로 변환 한 다음 std::move을 사용해야합니다.

질문 : 가능한 모든 파생 클래스에 캐스트를 시도한 후에 std::move을 사용해야합니까?

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

그냥 항상 Base을 만드는 :

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 

답변

1

이 나쁜 인터페이스입니다. 기본 하위 객체를 잘라내는데, 이것은 진실한 다형성 복사본이 아닙니다. 이를 수행하려면 Baseclone() 메소드를 추가해야합니다.

하지만 다형성을 사용자에게 맡기는 것이 더 간단합니다. 당신의 일은 안전한 인터페이스를 제공하는 것입니다. 그 인터페이스는해야한다 :

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

가 지금은, 사용자로, 그것에 대해 걱정 할 필요 인터페이스 디자이너로 당신없이 shared_ptr 내가 원하는대로 파생 형으로 제공 할 수

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

감사! 그러나 현재의 컨텍스트에서는 인터페이스에서 shared_ptr을 전달할 수 없습니다. 입력 인자가 initializer_list 인 값을 생성자 내부의 벡터로 전달합니다. 내 업데이트를 확인하십시오! –

0

당신 객체로 이동하려는 시도를 중단해야합니다. 일부 기본 클래스에 대한 포인터의 컨테이너를 갖고 싶지만 사용자가 저장 한 파생 클래스를 직접 할당하지 않으려면 할당을 수행하기 위해 삽입 함수를 사용해야합니다. 이 경우, 그것은 일반적인 표준 라이브러리 emplace 기능처럼 작동합니다 : 사용자가 그들이 container에 의해 만들어진 새로운 Derived로 이동하려는 Derived 인스턴스가

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>(); 

경우, 그들은 container.add<Derived>(std::move(theirDerivedInstance)); 할 것입니다.

+0

이것은 표준 라이브러리에 이상한 점 중 하나입니다. 'is_base_of'는 종종 사물/모호한 기지와 일치하기 때문에 종종 잘못된 것처럼 보입니다 ...당신이 원했던 것은'is_convertible '입니다. 의도를 전혀 전달하지 않습니다 ... – Barry

+0

@ 배리 : 실제로 뭔가의 전환이 가능할 수 있기 때문에 둘 다 필요합니다. –

+1

@ NicolBolas 참조가 아닌 포인터를 확인하는 경우가 아닙니다. –

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를 쓰면 constructible을 추가 할 수 있습니다 ... 또한 emplace를 쓸 때, 나는 종종 완벽한 전달의 공통적 인 불완전 성을 수정하기 때문에 initial-argument list 생성자를 추가합니다.

+0

감사합니다. 메소드 add는 완벽하게 작동합니다! 템플릿이 작업을 완료했습니다. 하지만 initializer_list 생성자에 대한 내 업데이트를 확인할 수 있습니까? –

+0

@AlfredHichkog 새로운 질문이 있으면 편집 버튼이 아닌 질문 버튼을 사용하십시오. – Yakk