Reputation: 431
Whether by good design or not, my main()
method declares a variable that needs to expect any iterator that meets the BidirectionalIterator
definition (particularly list<>::iterator
or vector::iterator
) based on what datastructure is being used (determined by input arguments). Since I cannot template the main()
method, how does one accomplish this?
For example, instead of:
int main(int argc, const char* argv[]) {
vector<Person>::iterator iterator0;
list<Person>::iterator iterator1);
multimap<string, Person>::iterator iterator2;
}
Since all there iterators meet the requirement of BidirectionalIterator
, I want to do:
int main(int argc, const char* argv[]) {
bidirectionaliterator iterator0;
}
Then, I don't have to check continually what kind of datastructure the program is using with if-statements to store an iterator and use the iterator I want.
Upvotes: 2
Views: 149
Reputation: 4708
I think what you're looking for is Type Erasure. You can think of it as the inverse of an interface. You can imagine an interface as sitting at the bottom of a class, determining what methods it needs to provide. A type erased object sort of attaches at the top, and "extracts" certain methods, bringing in duck-typing. There's an upcoming Boost Type Erasure library which will make this an easy to use concept.
Here's how it works in plain C++11 (It can easily be made to work in pre-11 C++ as well, but I wanted
to use unique_ptr
):
#include<iostream>
#include<vector>
#include<list>
#include<memory>
using namespace std;
class Person{};
template <class T>
class TypeErasedBidirectionalIterator {
public:
virtual void operator++()=0;
virtual void operator--()=0;
virtual T& operator*()=0;
};
template <class T, class Iterator>
class BidirectionalIteratorAdaptor: public TypeErasedBidirectionalIterator<T> {
Iterator it;
public:
BidirectionalIteratorAdaptor(Iterator it): it(it){}
void operator++(){it++;}
void operator--(){it--;}
T& operator*(){*it;}
};
template <class Iterator>
unique_ptr<BidirectionalIteratorAdaptor<typename Iterator::value_type,Iterator> > makeIterator(Iterator it) {
typedef typename Iterator::value_type T;
return unique_ptr<BidirectionalIteratorAdaptor<T,Iterator> >(new BidirectionalIteratorAdaptor<T,Iterator>(it));
}
typedef TypeErasedBidirectionalIterator<Person> PersonIterator;
typedef unique_ptr<PersonIterator> PersonIteratorPtr;
int main() {
vector<Person> vec;
list<Person> lst;
lst.push_back(Person());
PersonIteratorPtr it = makeIterator(vec.begin());
it = makeIterator(lst.begin());
++*it;
--*it;
**it;
}
Note that by wrapping the PersonIteratorPtr
inside another class that exposes the methods
directly you can get rid of the pointer-like behavior, but I didn't want to complicate the proof-of-concept even more.
Upvotes: 1
Reputation: 52365
It sounds like you want typedefs:
typedef std::vector<Person> person_container;
typedef person_container::iterator mybidirectionaliterator;
Then anytime you want to change the underlying container, all you have to do is change std::vector<Person>
to something else. Although, you still cannot assign any iterator here, you have to use iterators that are compatible. However, are you familiar with the auto
keyword in C++11? A lot of times you never have to write out the iterator anymore, i.e. auto myiter = some_container.begin();
would be sufficient.
Also, to get a better answer, it helps to show how you are using the iterator rather than just declaring it.
Upvotes: 1