class X {
public:
typedef std::list<int> Container;
// (1)
const Container& GetElements() const;
// (2)
Container::iterator ElementBegin();
Container::iterator ElementEnd();
// (3)
CustomIterator GetElementIterator();
private:
Container m_container;
};
I'm looking for a consistent and clean way of providing iterators to encapsulated containers to the caller. I came up with the three ideas marked in the source code above.
Container
reference is const, you'll only be able to use const_iterator
. Returning the reference non-const is bad, because the container itself could be modified (e.g. clear()
).size()
method (like GetElementCount()
). iterator::distance()
could be used, but that may be inefficient for some containers (where operator++
/--
is called repeatedly to calculate the distance).next()
etc. Still an own size()
method is required.I highly bet there're nicer solutions, so if you know any, I'd be glad to see them.
A mix of (2) and (3) would probably be what I'd do :
class X {
public :
typedef std::list<int> ElementContainer;
typedef ElementContainer::size_type ElementSizeType;
typedef ElementContainer::iterator ElementIterator;
typedef ElementContainer::const_iterator ConstElementIterator;
ElementIterator elementBegin() { return m_container.begin(); }
ElementIterator elementEnd() { return m_container.end(); }
ConstElementIterator elementBegin() const { return m_container.begin(); }
ConstElementIterator elementEnd() const { return m_container.end(); }
ElementSizeType elementSize() const { return m_container.size(); }
private :
ElementContainer m_container;
};
It still leaves room to write custom iterators (by changing the typedef
s), but as long as the ones provided by the container are ok, they can be used.
I would use these names instead : iterator
, const_iterator
, begin
, end
, cbegin
, cend
and size()
as:
class X
{
public :
typedef std::list<int>::iterator iterator;
typedef std::list<int>::const_iterator const_iterator ;
iterator begin() { return m_container.begin(); }
iterator end() { return m_container.end(); }
const_iterator cbegin() const { return m_container.begin(); }
const_iterator cend() const { return m_container.end(); }
size_t size() const { return m_container.size(); }
private :
std::list<int> m_container;
};
And if you can use C++0x, then use m_container.cbegin()
and m_container.cend()
as:
const_iterator cbegin() const { return m_container.cbegin(); }
const_iterator cend() const { return m_container.cend(); }
I can't think of much cleaner methods; you might consider the lightweight (4) solution giving access with
const Container& container() const { return m_container; }
I would prefer (3) since the container type becomes fully encapsulated, i.e. your type does not necessarily require inclusion of and you can change the container type without recompiling depending modules.
Off course, the simplest is this :
class X {
public:
typedef std::list<int> Container;
Container m_container;
};
but that makes your class X
obsolete.
Other then that, if you really like your class, then add next methods :
Container::const_iterator ElementBegin() const;
Container::const_iterator ElementEnd() const;
int size() const;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With