2017-11-25 186 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