2016-09-28 7 views
3

Предполагая, что этот код:вектор unique_ptr, наследование?

class Parent {} 
class Child : public Parent {} 

static std::vector<std::unique_ptr<Child>> Foo(); 

Есть более простой способ, чтобы написать эту функцию:

std::vector<std::unique_ptr<Parent>> Bar() { 
    auto children = Foo(); 

    std::vector<std::unique_ptr<Parent>> parents; 
    result.insert(result.end(), std::make_move_iterator(children.begin()), 
       std::make_move_iterator(children.end())); 
    return parents; 
} 

Это не работает:

std::vector<std::unique_ptr<Parent>> Bar() { 
    return Foo(); // Compiler error: cannot convert from vector<...> to vector<...> 
} 
+2

FYI: Это точно так же, проблема с исходными указателями. – rubenvb

+0

@ rubenvb: Вы правы, я никогда не замечал ... :) –

ответ

4

типы различны. Foo() возвращение std::vector<std::unique_ptr<Child>> пока Bar() возвращение std::vector<std::unique_ptr<Parent>>. Невозможно обойти это. Однако вместо:

std::vector<std::unique_ptr<Parent>> Bar() { 
    auto children = Foo(); 

    std::vector<std::unique_ptr<Parent>> parents; 
    result.insert(result.end(), std::make_move_iterator(children.begin()), 
       std::make_move_iterator(children.end())); 
    return parents; 
} 

Вы можете сделать:

std::vector<std::unique_ptr<Parent>> Bar() { 
    auto tmp = Foo(); 
    return {std::make_move_iterator(tmp.begin()), std::make_move_iterator(tmp.end()));} 
} 
3

Ну мы можем написать шаблонный в другом месте:

templace<class Cin> 
struct move_from_c{ 
    Cin* c; 
    template<class Cout> 
    operator Cout()&&{ 
    using std::begin; using std::end; 
    return {std::make_move_iterator(begin(*c)), std::make_move_iterator(end(*c))}; 
    } 
}; 
template<class C, class dC=std::remove_reference_t<C>> 
move_from_c<dC> move_from(C&&c){ 
    return {std::addressof(c)}; 
} 

И тогда ваша функция:

std::vector<std::unique_ptr<Parent>> Bar() { 
    return move_from(Foo()); 
} 

Это разделяет детали реализации из бизнес-логика Bar. (Как происходит переход от решения, чтобы перейти от него).

1

Предложите использовать идиом дескриптора/тела и реализовать полиморфизм как деталь реализации класса дескриптора.

Это дает семантику значений (работает в контейнерах), а также позволяет легко реализовать реляционные операторы:

#include <vector> 
#include <memory> 

class ParentImpl { 
public: 
    virtual ~ParentImpl() = default; 
    virtual void foo() {} 
}; 
class ChildImpl : public ParentImpl {}; 

class ParentHandle 
{ 
public: 
    using ptr_type = std::unique_ptr<ParentImpl>; 

    // construct from ptr_type 
    ParentHandle(ptr_type ptr = nullptr) : ptr_(std::move(ptr_)) {} 

    // public interface defers to polymorphic implementation 
    void foo() 
    { 
    ptr_->foo(); 
    } 

private: 
    std::unique_ptr<ParentImpl> ptr_; 
}; 

static std::vector<ParentHandle> Foo() 
{ 
    std::vector<ParentHandle> result; 

    result.emplace_back(std::make_unique<ParentImpl>()); 
    result.emplace_back(std::make_unique<ChildImpl>()); 

    return result; 
} 
2

Несколько «простые» способы:

#include <algorithm> 
#include <iterator> 

std::vector<std::unique_ptr<Base>> f1(std::vector<std::unique_ptr<Derived>> v) 
{ 
    std::vector<std::unique_ptr<Base>> result; 
    result.reserve(v.size()); 
    std::move(v.begin(), v.end(), std::back_inserter(result)); 
    return result; 
} 

std::vector<std::unique_ptr<Base>> f2(std::vector<std::unique_ptr<Derived>> v) 
{ 
    return {std::make_move_iterator(v.begin()), std::make_move_iterator(v.end())}; 
} 
+0

[Demo] (http://ideone.com/dzxLLB) –