2017-11-25 187 views
0

在最后一天左右,我一直在为此而烦恼。我想弄清楚如何在隐藏类正在使用的容器类型时从类中返回迭代器。一个例子是我有一个类画布,它拥有相同界面的小部件,并且它们被私人存储在一个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; 
} 

基本上,我想以某种别名mWidget.begin()和mWidget.end()向上穿过帆布::开始()和Canvas: :结束()。用户知道迭代器是一个小部件,他们只是不需要知道迭代器是std :: vector :: iterator。我试图使用PIMPL来隐藏我的实现,并保存关于事物如何存储在类中的信息。

我似乎无法找到正确的“公式”。我已经看过类型擦除,并试图通过接口返回函数指针,但我似乎无法想出一种方法来保持std :: vector :: iterator不在标题中,以及我迄今为止所看到的所有内容看起来这似乎不符合我想要做的。有人能指引我朝着正确的方向吗?阅读材料?有没有我失踪的概念?噢 - 我已经看到了一些有用的例子,我无法弄清楚如何在我的情况下工作。我想避免这种情况,因为我试图减少外部依赖。

非常感谢您提前!

+0

你不能只是*文件*,你的WidgetIterator是一个*未指定的*随机访问迭代器(或任何你想要的迭代器类)?例如,标准库不会对其迭代器类型施加特殊的限制,除了迭代器概念所强加的类型(例如,vector <> :: iterator可能只是一个指针)... –

+1

实现您自己的迭代器。它可以在内部使用std :: vector迭代器,或者不使用。迭代器必须实现很少的“功能”才能使用。 –

+0

所以迭代是一个非常密集的过程,涉及与迭代器的大量交互。你认为重击性能很好吗? – Yakk

回答

0

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

这足以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() 
); 
} 

和小露出。

如果你想更进一步,并能够包装非随机访问容器,这是更多的工作。

+0

感谢您提供非常详细的答案。它给了我这个概念,并指出了我想要走的方向。非常感激! – tenspd137