While discussing multimap
with my students, I noticed a small change that could cut out a bit of boilerplate, and was wondering if anyone had suggested it to the standard committee, and if so what the response was.
The canonical method of iterating over an equal range is (taken from cplusplus.com):
// multimap::equal_range
#include <iostream>
#include <map>
int main ()
{
std::multimap<char,int> mymm;
mymm.insert(std::pair<char,int>('a',10));
mymm.insert(std::pair<char,int>('b',20));
mymm.insert(std::pair<char,int>('b',30));
mymm.insert(std::pair<char,int>('b',40));
mymm.insert(std::pair<char,int>('c',50));
mymm.insert(std::pair<char,int>('c',60));
mymm.insert(std::pair<char,int>('d',60));
std::cout << "mymm contains:\n";
for (char ch='a'; ch<='d'; ch++)
{
std::pair <std::multimap<char,int>::iterator,std::multimap<char,int>::iterator> ret;
ret = mymm.equal_range(ch);
std::cout << ch << " =>";
for (std::multimap<char,int>::iterator it=ret.first; it!=ret.second; ++it)
std::cout << ' ' << it->second;
std::cout << '\n';
}
return 0;
}
You cannot use a range based for loop directly in this case because the return type of equal_range is a pair<multimap<K,V>::iterator, multimap<K,V>::iterator>
. However, a simple wrapping struct should allow this:
template <typename T>
struct abstract_collection {
abstract_collection(pair<T, T> its)
: m_begin(its.first),
m_end(its.second) {}
abstract_collection(T begin, T end)
: m_begin(begin),
m_end(end) {}
T begin() const { return m_begin; }
T end() const { return m_end; }
T m_begin;
T m_end;
};
Combined with adding a function to the multimap (and others) API to return iterators in this structure, rather than in a pair.
template<typename K, typename V, typename C, typename A>
auto multimap<K, V, C, A>::equal_range_c(K const& k) -> abstract_collection<iterator> {
return equal_range(k);
}
Or alternatively overloading a version of std::begin
and std::end
that takes a pair of iterators should work as well:
template <typename T>
T begin(pair<T, T> p) { return p.first; }
template <typename T>
T end(pair<T, T> p) { return p.second; }
Has these ideas surfaced before, and if so, what was the committee response? Are they just unworkable or undesirable for some reason I'm not seeing?
(Note, the code was written without attempting to compile or check for expositional purposes only. It's probably wrong. And it doesn't contain typechecking to constrain to iterators only as it ought to, as that was added complexity that didn't serve to explain the idea.)
This is what boost::iterator_range
accomplishes, which was adopted into the range library TS as ranges::iterator_range
. That TS is to be incorporated sometime after C++17.