2012-06-22 33 views
6

我有一些類,因各種原因離開這個討論的範圍,我不能修改(不相關的實施細節省略)進行迭代是我處理的很多類似的'Foo'和'Bar'類,它們都是從其他地方生成的代碼和我不想進行子類化的東西等等。)適應非可迭代的容器通過自定義模板化的迭代器

[編輯:澄清 - 儘管有很多類似'Foo'和'Bar'類,它保證每個「外部」類都有getter和size方法。只有getter方法名稱和返回類型對於每個「外部」纔會有所不同,這取決於它所包含的「內部」類型是什麼。

所以,如果我有巴茲其中包含QUUX情況下,會有QUUX &巴茲:: get_quux(爲size_t指數),併爲size_t巴茲:: size_quux()。]

鑑於酒吧類的設計,你不能輕易地在STL算法使用(例如for_each的,find_if等),而且必須做必要的循環,而不是採取功能性辦法(爲什麼我更喜歡後者的原因也超出範圍的討論):

Bar b; 
size_t numFoo = b.size_foo(); 
for (int fooIdx = 0; fooIdx < numFoo; ++fooIdx) { 
    Foo& f = b.get_foo(fooIdx); 
    /* ... do stuff with 'f' ... */ 
} 

所以...我從來沒有創建過自定義迭代器,並且在閱讀了關於SO的各種問題/答案後首先,自定義迭代器機制(注意:'function'和'bind'的所有用法都來自std :: tr1在MSVC9):

// Iterator mechanism... 
template <typename TOuter, typename TInner> 
class ContainerIterator : public std::iterator<std::input_iterator_tag, TInner> { 
    public: 
    typedef function<TInner& (size_t)> func_type; 

    ContainerIterator(const ContainerIterator& other) : mFunc(other.mFunc), mIndex(other.mIndex) {} 

    ContainerIterator& operator++() { ++mIndex; return *this; } 

    bool operator==(const ContainerIterator& other) { 
     return ((mFunc.target<TOuter>() == other.mFunc.target<TOuter>()) && (mIndex == other.mIndex)); 
    } 

    bool operator!=(const ContainerIterator& other) { return !(*this == other); } 

    TInner& operator*() { return mFunc(mIndex); } 

    private: 
    template<typename TOuter, typename TInner> 
    friend class ContainerProxy; 

    ContainerIterator(func_type func, size_t index = 0) : mFunc(func), mIndex(index) {} 

    function<TInner& (size_t)> mFunc; 
    size_t mIndex; 
}; 

接着,通過我得到的機制代表開始和內部容器的端部有效迭代:

// Proxy(?) to the outer class instance, providing a way to get begin() and end() 
// iterators to the inner contained instances... 
template <typename TOuter, typename TInner> 
class ContainerProxy { 
    public: 
    typedef function<TInner& (size_t)> access_func_type; 
    typedef function<size_t()> size_func_type; 

    typedef ContainerIterator<TOuter, TInner> iter_type; 

    ContainerProxy(access_func_type accessFunc, size_func_type sizeFunc) : mAccessFunc(accessFunc), mSizeFunc(sizeFunc) {} 

    iter_type begin() const { 
     size_t numItems = mSizeFunc(); 
     if (0 == numItems) return end(); 
     else return ContainerIterator<TOuter, TInner>(mAccessFunc, 0); 
    } 
    iter_type end() const { 
     size_t numItems = mSizeFunc(); 
     return ContainerIterator<TOuter, TInner>(mAccessFunc, numItems); 
    } 

    private: 
    access_func_type mAccessFunc; 
    size_func_type mSizeFunc; 
}; 

我可以以下列方式使用這些類

// Sample function object for taking action on an LMX inner class instance yielded 
// by iteration... 
template <typename TInner> 
class SomeTInnerFunctor { 
    public: 
    void operator()(const TInner& inner) { 
     /* ... whatever ... */ 
    } 
}; 

// Example of iterating over an outer class instance's inner container... 
Bar b; /* assume populated which contained items ... */ 
ContainerProxy<Bar, Foo> bProxy(
    bind(&Bar::get_foo, b, _1), 
    bind(&Bar::size_foo, b)); 
for_each(bProxy.begin(), bProxy.end(), SomeTInnerFunctor<Foo>()); 

根據經驗,這種解決方案的功能正常(減去任何複製/粘貼或錯別字我可能已經編輯以上爲簡潔時引入的)。

所以,最後,實際的問題:

我不喜歡需要使用綁定的()和_1佔位符,等等調用者。所有他們真正關心的是:外型,內型,外類型的獲取內部實例方法,外部類型的獲取數內實例方法。

有什麼辦法來「隱藏」在某種程度上的模板類的身體的綁定?我一直無法找到一個方法來分別提供的類型和單獨內方法的模板參數...

謝謝!
David

+0

如果你只是想要避免'std :: bind'和'std :: placeholders',你可以濫用這個事實,例如, 'stoo :: function '非常樂意吞噬指向成員函數的類型'Foo&(Bar :: *)(size_t)' – Managu

回答

2

或者,您可以始終以功能爲模板參數本身,如果他們有一個可預見的簽名:

template <typename TOuter, typename TInner, 
      TInner& (TOuter::*getfunc)(size_t)> 
class ContainerIterator 
{ 
public: 
    //... 
    TInner& operator*() {return mContainerRef.*getfunc(mIndex);} 
    //... 
}; 

template <typename TOuter, typename TInner, 
      size_t (TOuter::*sizefunc)(), 
      TInner& (TOuter::*getfunc)(size_t)> 
class ContainerProxy 
{ 
public: 
    //... 
    ContainerIterator<TOuter, TInner, getfunc> end() { 
     return ContainerIterator<TOuter, TInner, getfunc> 
        (mContainerRef, 
        mContainerRef.*sizefunc()); 
    } 
    //... 
}; 

int main() 
{ 
    Bar b; 
    ContainerProxy<Bar, Foo, &Bar::size_foo, &Bar::get_foo> proxy(b); 
    std::for_each(proxy.begin(), proxy.end(), /*...*/); 
} 

或者更進一步(http://ideone.com/ulIC7),並通過包裹起來在std::function傳遞功能:

template <typename TOuter, typename TInner> 
struct ContainerIterator : public std::iterator<std::input_iterator_tag, TInner> 
{ 
    TOuter& mContainerRef; 
    //... 
    typedef std::function<TInner& (TOuter*, size_t)> getfunc_type; 
    getfunc_type mGetfunc; 

    ContainerIterator(TOuter& containerRef, size_t index, getfunc_type const& getFunc) 
    /* ... */ {} 
    TInner& operator*() {return mGetfunc(&mContainerRef, mIndex);} 
    ContainerIterator<TOuter, TInner>& operator++() {++mIndex; return *this;} 

    // ... 
}; 

template <typename TOuter, typename TInner> 
struct ContainerProxy 
{ 
    TOuter& mContainerRef; 

    typedef std::function<size_t (TOuter*)> sizefunc_type; 
    sizefunc_type mSizefunc; 

    typedef std::function<TInner& (TOuter*, size_t)> getfunc_type; 
    getfunc_type mGetfunc; 

    ContainerProxy(TOuter& containerRef, sizefunc_type sizefunc, getfunc_type getfunc) 
    // ... 
    { 
    } 

    // ... 

    ContainerIterator<TOuter, TInner> end() const 
    { 
     return ContainerIterator<TOuter, TInner>(mContainerRef, 
               mSizefunc(&mContainerRef), 
               mGetfunc); 
    } 
}; 

int main() 
{ 
    Bar b=...; 

    ContainerProxy<Bar, Foo> proxy(b, &Bar::size_foo, &Bar::get_foo); 
    std::for_each(proxy.begin(), proxy.end(), /*...*/); 
} 
+0

我認爲這個答案大多數直接回答我的問題,因爲它內部隱藏了我用bind <>調用外部進行的函數<>構造,所以我會選擇它作爲「答案」。不過,我認爲我更喜歡您的LegacyContainerTraits方法,並且可能會實際使用該方法。非常感激! – DAldridge

3

您可以定義一個輔助模板結構來隱藏與Foo和Bar交互的實際機制。然後專注每個集裝箱:

// incomplete general case 
template <typename TOuter> struct LegacyContainerTraits; 

// Specialization for 'Bar' 
template <> struct LegacyContainerTraits<Bar> 
{ 
    // The inner type of 'Bar' is 'Foo' 
    typedef Foo inner_type; 

    static size_t get_size(Bar const& outer) {return outer.size_foo();} 
    static Foo& get_element(Bar const& outer, size_t index) { 
     return outer.get_foo(index); 
    } 
}; 

// Specialization for Baz 
template <> struct LegacyContainerTraits<Baz> 
{ 
    // The inner type of 'Baz' is 'Quux' 
    typedef Quux inner_type; 

    static size_t get_size(Baz const& outer) {return outer.size_quux();} 
    static Quux& get_element(Baz const& outer, size_t index) { 
     return outer.get_quux(index); 
    } 
}; 

然後在ContainerProxy/ContainerIterator,而不是存儲和使用功能,您只需存儲參考/複製到容器和調用的LegacyContainerTraits適當的專業化。事實上,有沒有真正需要任何ContainerProxy可言:

template <typename TOuter> class LegacyContainerIterator; 
template <typename TOuter> LegacyContainerIterator<TOuter> begin(TOuter&); 
template <typename TOuter> LegacyContainerIterator<TOuter> end(TOuter&); 

template <typename TOuter> 
class LegacyContainerIterator : 
    public std::iterator<std::random_access_iterator_tag, 
         typename LegacyContainerTraits<TOuter>::inner_type > 
{ 
private: 
    ... 
    friend LegacyContainerIterator<TOuter> begin<TOuter>(TOuter&); 
    friend LegacyContainerIterator<TOuter> end<TOuter>(TOuter&); 
    LegacyContainerIterator(TOuter& containerRef, size_t index) ... {}; 
    ... 

public: 
    ... 
    typename LegacyContainerTraits<TOuter>::inner_type& operator*() { 
     return LegacyContainerTraits<TOuter> 
      ::get_element(mContainerRef, mIndex); 
    } 
    ... 
}; 

template <typename TOuter> 
LegacyContainerIterator<TOuter> begin(TOuter& containerRef) 
{ 
    return LegacyContainerIterator<TOuter>(containerRef, 0); 
} 

template <typename TOuter> 
LegacyContainerIterator<TOuter> end(TOuter& containerRef) 
{ 
    return LegacyContainerIterator<TOuter>(
       containerRef, 
       LegacyContainerTraits<TOuter>::get_size(containerRef)); 
} 

然後你就可以在一個循環或算法很容易使用免費的功能。即使是在基於範圍的for循環:

Bar b=...; 

for (auto it=begin(b); it!=end(b); ++it) {...} 

for (auto f : b) {...} 

std::for_each(begin(b), end(b), ...); 

更充實的版本:http://ideone.com/JA9hC

+0

如何擴展這個解決方案來處理常量引用作爲輸入? (即採用(const TOuter&))的begin <>和end <>實現)。單獨的ConstLegacyIteratorTraits <>類,因爲它最終歸結爲具有'const TOuter&mContainerRef;'? – DAldridge

+0

我想我會添加'LegacyIteratorTraits '專業化,將'inner_type'定義爲'const Foo'。看起來像其他所有的東西都可以工作:http://ideone.com/8iVVg – Managu

+0

或者甚至是'LegacyIteratorTraits '的部分專業化,它可以很爽地''const_cast'解決問題:http://ideone.com/01gBW – Managu

2

下面是Managu解決方案變體的完整實現。 (嗯,我知道我沒有實現所需的所有必需的迭代器函數來獲得一個真正的隨機訪問迭代器..執行其餘的是讀者的練習。)。

#include <vector> 
#include <iostream> 
#include <iterator> 

class Foo { 
    public: 
    Foo(int ii) : i(ii) {} 
    Foo() : i() {} 
    int i; 
}; 

class Bar { 
    public: 
    Bar() : f1(1), f2(2), f3(3) {} 

    Foo& get_foo(size_t i) { 
     if(i==0) return f1; 
     if(i==1) return f2; 
     return f3; 
    } 

    size_t n_foo() const { return 3; } 

    Foo f1; 
    Foo f2; 
    Foo f3; 
}; 


template< 
    typename INNER, 
    typename OUTER, 
    size_t(OUTER::*COUNTER)() const, 
    INNER&(OUTER::*ACCESSOR)(size_t) > 
class ContainerProxy { 
    public: 
    ContainerProxy(OUTER * o) : outer(o) {} 

    OUTER * outer; 
    struct Iterator { 
     typedef std::random_access_iterator_tag iterator_category; 
     typedef INNER      value_type; 
     typedef ptrdiff_t     difference_type; 
     typedef INNER*      pointer; 
     typedef INNER&      reference; 

     ContainerProxy * container; 
     size_t index; 

     Iterator(ContainerProxy * c, size_t i) : container(c), index(i) {} 
     Iterator& operator++() { index++; return *this; } 
     INNER& operator*() { return (container->outer->*ACCESSOR)(index); } 

     difference_type operator-(const Iterator other) const { return index-other.index; } 
    }; 

    Iterator begin() { return Iterator(this,0); } 

    Iterator end() { return Iterator(this, (outer->*COUNTER)()); } 
}; 



int main() { 
    Bar bar; 
    ContainerProxy<Foo,Bar, &Bar::n_foo, &Bar::get_foo> container(&bar); 

    std::vector<Foo> v(3); 
    std::copy(container.begin(), container.end(), v.begin()); 

    std::cout<<v[0].i<<std::endl; 
    std::cout<<v[1].i<<std::endl; 
    std::cout<<v[2].i<<std::endl; 
} 
+0

使迭代器成爲一個嵌套類是一個很好的接觸。爲什麼我沒有想到這個? – Managu