Mam kilka klas, które z różnych powodów są poza zakresem tej dyskusji, nie można zmodyfikować (szczegóły nieistotne wdrożeniowe pominięta):Adaptacja nie iterable pojemników należy powtórzyć za zwyczaj templatized iterator
class Foo { /* ... irrelevant public interface ... */ };
class Bar {
public:
Foo& get_foo(size_t index) { /* whatever */ }
size_t size_foo() { /* whatever */ }
};
(Nie jest wiele podobnych klas "Foo" i "Bar", z którymi mam do czynienia, i to wszystko jest generowany kod z innego miejsca i rzeczy, których nie chcę podklasy itp.)
[Edytuj: wyjaśnienie - choć jest wiele podobne klasy "Foo" i "Bar" gwarantują, że każda klasa "zewnętrzna" będzie miała metody gettera i wielkości. Tylko nazwa metody getter i typ powrotu będą się różnić dla każdego "zewnętrznego", w zależności od tego, jaki jest "wewnętrzny" typ zawarty w nim.
Więc jeśli mam Baz który zawiera instancje quux, nie będzie quux & Baz :: get_quux (size_t indeksu), a size_t Baz :: size_quux().]
Ze względu na konstrukcję klasy Bar , nie możesz z łatwością używać go w algorytmach STL (np. for_each, find_if, itp.), i musi wykonywać pętle bezwzględne, zamiast przyjmować podejście funkcjonalne (powody, dla których wolę to ostatnie jest również poza zakresem tej dyskusji):
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' ... */
}
Więc ... Nigdy nie stworzyłem niestandardowego iteratora i po przeczytaniu różnych pytań/odpowiedzi na SO o iterator_traits i tym podobne, wpadłem na to (obecnie na wpół wypalone) "rozwiązanie":
Po pierwsze, niestandardowy mechanizm iteratora (UWAGA: wszystkie zastosowania "funkcji" i "bindowania" pochodzą ze std :: tr1 w 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;
};
Następnie mechanizm, dzięki któremu mogę ważne iteratory reprezentujący początek i koniec pojemnika wewnętrznego:
// 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;
};
mogę korzystać z tych klas w następujący sposób:
// 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>());
Empirycznie to rozwiązanie działa poprawnie (pomijając wszelkie kopie/wklejanie lub literówki, które mogłem wprowadzić podczas edycji powyższego dla zwięzłości).
Więc wreszcie rzeczywiste pytanie:
nie lubię wymagające użycia bind() i _1 Symbole zastępcze, etcetera przez dzwoniącego. Wszystko, na czym im zależy, to: typ zewnętrzny, typ wewnętrzny, metoda typu zewnętrznego do pobierania instancji wewnętrznych, metoda typu zewnętrznego do pobierania zliczeń wewnętrznych.
Czy istnieje sposób, aby jakoś "ukryć" wiązanie w ciele klas szablonów? Nie mogłem znaleźć sposobu na osobne dostarczanie parametrów szablonu dla typów i metod wewnętrznych oddzielnie ...
Dzięki!
David
Jeśli wszystko, czego szukasz, to unikanie 'std :: bind' i' std :: placeholders', możesz nadużywać faktu, że np. 'std :: function' jest całkowicie zadowolony z połknięcia funkcji typu wskaźnik-do-członka 'Foo & (Bar :: *) (size_t)' –
Managu