【问题标题】:How can I get a derived iterator from a vector of unique_ptr<Base>?如何从 unique_ptr<Base> 的向量中获取派生迭代器?
【发布时间】:2014-03-27 19:00:42
【问题描述】:

我有一个基类 (Container),它在 std::unique_ptr&lt;Base&gt; 的向量中保存各种对象 (Base 项),我有一个派生类 (FolderOfFolders),它只保存特定项 (@ 987654325@ 项)。我想使用基于范围的 for 来遍历该特定对象(FolderOfFolder),但我不知道该怎么做。使用 range-based-for 我可以取回 std::unique_ptr&lt;Base&gt; 项目,但我想要 const Folder* 项目。我知道我需要一个 begin()、end() 对来为基于范围的 for 提供迭代器,但我需要从 std::unique_ptr&lt;Base&gt;const Folder* 的转换。这有可能吗?

class Container : public Base {
  public:
    // I want to get back these as Folder* from FolderOfFolders
    std::vector<std::unique_ptr<Base>> items;
};

class Folder : public Container {
  public:
   // I want to call this when I traverse FolderOfFolders with range-based-for
   void folderMethod();
   ...
};

class SystemFolder : public Folder {
  public:
   ...
};

class UserFolder : public Folder {
  public:
   ...
};

class FolderOfFolders : public Folder {
  public:

    // I know that every item is a Folder in items
    // can I get an iterator to a Folder* ?!
    iterator begin() {
      //  ???
      return items.begin();
    }
    iterator end() {
       // ???
       return items.end();
    }
};

我想把它当作

FolderOfFolders folderOfFolders;
for (folder : folderOfFolders)
  folder->folderMethod();

【问题讨论】:

  • 您是说要确保指针的类型为Folder* 还是只从迭代器中获取const Folder*
  • 在我看来,您需要创建自己的自定义迭代器,它包装了向量迭代器,并具有不同的返回类型。
  • 如果您声明Folder::folderMethod()items,您的问题会更清楚。还有Base
  • @Industrial-antidepressant 顺便说一句,您需要这样做的事实表明了糟糕的设计,X vs Y 问题。如您所见,除非您知道类型,否则持有 Base 对象的集合是没有用的,所以只知道该怪异的类型。恕我直言,您应该拥有template &lt;class T&gt; Container : public Container&lt;Base&gt; 并使用类型特征is_base_of。 Container 应该是公开基迭代器的特化。每个人都赢了。
  • @Industrial-antidepressant 这只是意味着你没有使用多态性,而是试图在你的代码中重现 RTTI。我的更改只会使您的代码更加灵活。允许你的“枚举”类型有一个“contiainer”位(而不是它自己的枚举),你会变得更加灵活。但这绝对是错误的设计,恕我直言

标签: c++ c++11 vector iterator unique-ptr


【解决方案1】:
#include <boost/iterator/transform_iterator.hpp>

struct Downcast
{
  typedef const Folder* result_type;

  result_type operator()(const std::unique_ptr<Base>& p) const
  { return static_cast<const Folder*>(p.get()); }
};

class FolderOfFolders : public Folder {
  public:

    typedef boost::transform_iterator<Downcast, decltype(items)::iterator> iterator;

    iterator begin() {
      return iterator(items.begin(), Downcast());
    }
    iterator end() {
      return iterator(items.end(), Downcast());
    }
};

注意因为FolderOfFolders::iterator::operator* 按值返回const Folder*,这意味着FolderOfFolders::iterator::reference 不是引用类型,因此迭代器只是一个InputIterator,而不是像底层vector::iterator 那样的RandomAccessIterator,但这足以与范围一起使用- 基于for

但是,这种“一切都是Base,所有集合都是Base 的集合”闻起来像Java。有更好的设计。即使您必须存储 Base 的容器,您也可以这样做,而不必更改迭代器类型:

const Folder& asFolder(const Base& b) {
  return dynamic_cast<const Folder&>(b); 
}

class FolderOfFolders : public Folder {
  public:

    typedef decltype(items)::iterator iterator;

    iterator begin() {
      return items.begin();
    }
    iterator end() {
      return items.end();
    }
};

...

for (auto& base_ptr : folderOfFolders)
  asFolder(*base_ptr).folderMethod();

【讨论】:

    【解决方案2】:

    您的解决方案是参考:

    for (auto &folder : folderOfFolders)
        folder->folderMehod();
    

    std::unique_ptr&lt;&gt; 对象不可复制,但您可以将迭代与引用绑定。

    【讨论】:

    • 这避免了尝试复制folder,但它无助于调用Folder::folderMethod(),因为folderstd::unique_ptr&lt;Base&gt;
    • 对不起,我的错,folderMethod();是在文件夹类而不是在 Base
    猜你喜欢
    • 1970-01-01
    • 2015-08-08
    • 2020-01-08
    • 2014-07-21
    • 2012-07-17
    • 2014-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多