如何在自定义类中隐藏迭代器和容器实现(无增强)

Posted

技术标签:

【中文标题】如何在自定义类中隐藏迭代器和容器实现(无增强)【英文标题】:How to hide Iterator and container implemtation in a custom class (w/o boost) 【发布时间】:2017-11-25 16:04:36 【问题描述】:

过去一天左右,我一直在为此绞尽脑汁。我试图弄清楚如何在隐藏类正在使用的容器类型的同时从类中返回迭代器。一个例子是我有一个类画布,它包含具有相同接口的小部件,并且它们私下存储在 std::vector 中。所以....

简化代码

class Canvas  
  
public:  
    WidgetIterator begin();  
    WidgetIterator end();  
private:
    class Impl;
    std::unique_ptr<Impl> mImpl;  
;

class Canvas::Impl  

public:  
    std::vector<widget> mWidget;  
    //how to return mWidget.begin(), mWidget.end() up as widget iterator??  
  

usage:  
int main()  
  
    Canvas canvas();
    ...Fill canvas with widgets...  
    //iterate over widgets  
    for(auto& a_widget : canvas)  
      
        //do something with / to a_widget.  User does not know or care  
        //that underlying iterator is std::vector<widget>::iterator 
      
    ...Do more stuff....  
    return 0;  
  

基本上,我想通过 Canvas::begin() 和 Canvas::end() 以某种方式为 mWidget.begin() 和 mWidget.end() 起别名。用户知道迭代器是一个小部件,他们只是不需要知道迭代器是 std::vector::iterator。我正在尝试使用 PIMPL 隐藏我的实现并保留有关事物如何在类中存储的信息。

我似乎找不到正确的“公式”。我已经查看了类型擦除并尝试通过接口返回函数指针,但我似乎想不出一种方法将 std::vector::iterator 保留在标头之外,以及到目前为止我在查找这个似乎不适合我想要做的事情。有人可以指出我正确的方向吗?阅读材料?有没有我缺少的概念?哦 - 我已经看到一些使用提升的例子,我无法弄清楚如何在我的情况下工作。我想避免这种情况,因为我试图减少外部依赖。

【问题讨论】:

你不能只是 document 你的 WidgetIterator 是一个 unspecified 随机访问迭代器(或任何你想要的迭代器类别)吗?例如,标准库没有对其迭代器类型施加特殊限制,除了迭代器概念施加的限制(例如,vector::iterator 可能只是一个指针)... 实现你自己的迭代器。它可以在内部使用 std::vector 迭代器,也可以不使用。迭代器必须实现很少的“功能”才能使用。 所以迭代是一个非常密集的过程,涉及到与迭代器的大量交互。您是否可以接受沉重的性能打击? 【参考方案1】:

键入擦除迭代器的最简单方法是编写输入迭代器生成器。

这足以for(:)循环,但不是所有其他算法,并且可以轻松包装任何随机访问容器:

template<class T>
struct gen_iterator_t 
  std::function<T(std::size_t)> f;
  std::size_t n = 0;
  gen_iterator& operator++()  ++n; return *this; 
  gen_iterator operator++(int)  auto r = *this; ++*this; return r; 
  T operator*()  return f(n); 
  gen_iterator_t( gen_iterator_t const& )=default;
  gen_iterator_t( gen_iterator_t && )=default;
  gen_iterator_t& operator=( gen_iterator_t const& )=default;
  gen_iterator_t& operator=( gen_iterator_t && )=default;
  gen_iterator_t()=default;

  explicit operator bool() const  return static_cast<bool>(f); 

  gen_iterator_t( std::function<T(std::size_t)> fin, std::size_t i ):
    f(fin), n(i)
  

  friend bool operator==( gen_iterator_t const& lhs, gen_iterator_t const& rhs ) 
    if (!lhs && !rhs) return true;
    if (!lhs || !rhs) return false;
    return lhs.n == rhs.n;
  
  friend bool operator==( gen_iterator_t const& lhs, gen_iterator_t const& rhs ) 
    return !(lhs==rhs);
  
;

然后我们写range:

template<class It>
struct range_t 
  It b, e;
  It begin() const  return b; 
  It end() const  return e; 
;
template<class It>
range_t<It> range( It s, It f )  return s,f; 

并生成范围:

template<class F>
range_t< gen_iterator_t< std::result_of_t<F&(std::size_t)> > >
generate_range( F f, std::size_t start, std::size_t finish ) 
  return  f, start, f, finish ;

现在你的班级公开了

class Canvas 
public:
  range_t< gen_iterator_t< widget& > > get_widgets();
;

实现为

range_t< gen_iterator_t< widget& > > Canvas::get_widgets() 
  return generate_range(
    [this]( std::size_t n )->widget&  return mImpl->mWidget[n]; ,
    0, mImpl->mWidget.size()
  );

几乎没有暴露。

如果您想更进一步,让它能够包装非随机访问容器,那就需要做更多的工作了。

【讨论】:

感谢您非常详细的回答。它给了我这个概念,并为我指明了我想去的方向。非常感谢!

以上是关于如何在自定义类中隐藏迭代器和容器实现(无增强)的主要内容,如果未能解决你的问题,请参考以下文章

python2.7 可迭代对象迭代器和迭代的概念说明

Python中迭代器&生成器的“奇技淫巧“

Python中迭代器&生成器的“奇技淫巧“

在自定义容器视图控制器中调用 endEditing 会隐藏键盘但不会更改键盘响应器

Python中的迭代器和生成器,以及装饰

迭代器和增强for