简体   繁体   中英

vector of unique_ptr, inheritance?

Assuming this code:

class Parent {}
class Child : public Parent {}

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

Is there a simpler way to write this function:

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;
}

This doesn't work:

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

The types are different. Foo() returns std::vector<std::unique_ptr<Child>> while Bar() returns std::vector<std::unique_ptr<Parent>> . There's no way to circumvent that. However, rather than:

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;
}

You can do:

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

Well we can write boilerplate elsewhere:

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

And then your function is:

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

This splits details of implementation from business logic of Bar . (How the move from is done is split from the decision to move from).

Several "simpler" ways:

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

Suggest you use a handle/body idiom and implement the polymorphism as an implementation detail of the handle class.

This gives you value semantics (works in containers) and also allows you to implement relational operators easily:

#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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM