Does it make sense to provide only constant iterators for the container?

I have a container similar to the following:

class MySpecialContainer { std::vector<std::tuple<InternalType, Type1, Type2>> _vec; }; 

where Type1 and Type2 can be used outside the container, and InternalType used only inside the container. To iterate over elements from the outside, I use a member function similar to the following:

 void MySpecialContainer::iterate(std::function<void(const Type1&, const Type2&)> fun) { for(auto& it : _vec) { fun(std::get<1>(it), std::get<2>(it)); } } 

As you can see, this approach has several limitations, such as the inability to iterate over the subrange or the inability to use non mutating std::algorithms .

Given that the MySpecialContainer elements MySpecialContainer not change externally for logical reasons, does it make sense to provide only const_iterator for this?

If the answer, if yes for the first question, is it better ...?

  • divide _vec into 2 containers, one for InternalType and one for std::pair<Type1, Type2> , synchronize them and just return const_iterator for the second vector

  • save the vector as it is now and create a custom iterator that provides only const Type1 and const Type2

+6
source share
2 answers

Just expose constant iterators in order. There is a standard for this standard, since std::set does this effectively. Technically, iterator and const_iterator can be of different types, but you are not allowed to change elements through any type of iterator, as this may violate the invariants for set .

+3
source

One of the options is to set iterators that provide access to certain fields of only your elements, for example:

 #include <vector> #include <tuple> #include <boost/iterator/transform_iterator.hpp> struct Type1 {}; struct Type2 {}; struct InternalType {}; class MySpecialContainer { typedef std::vector<std::tuple<InternalType, Type1, Type2>> Vec; Vec _vec; struct Extractor { std::tuple<Type1&, Type2&> operator()(Vec::value_type& t) const { return std::tie(std::get<1>(t), std::get<2>(t)); } std::tuple<Type1 const&, Type2 const&> operator()(Vec::value_type const& t) const { return std::tie(std::get<1>(t), std::get<2>(t)); } }; public: typedef boost::transform_iterator<Extractor, Vec::iterator> iterator; typedef boost::transform_iterator<Extractor, Vec::const_iterator> const_iterator; iterator begin() { return iterator{_vec.begin()}; } iterator end() { return iterator{_vec.end()}; } const_iterator begin() const { return const_iterator{_vec.begin()}; } const_iterator end() const { return const_iterator{_vec.end()}; } }; int main() { MySpecialContainer c; for(auto x : c) { } } 

Note that with non-const iterators you can update public values, because the trasform iterator returns a tuple of links.

+3
source

Source: https://habr.com/ru/post/974148/


All Articles