STL defines two flavors of the transform function
The first is For unary operators:
template <class InputIterator, class OutputIterator, class UnaryOperation>
OutputIterator transform (InputIterator first1, InputIterator last1,
OutputIterator result, UnaryOperation op);
And the second is for binary operators:
template <class InputIterator1, class InputIterator2,
class OutputIterator, class BinaryOperation>
OutputIterator transform (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, OutputIterator result,
BinaryOperation binary_op);
What is the most efficient implementation of a similiar function for a ternary operator?
EDIT:
Here is the trivial implementation I came up with, but isn't there a leaner and more elegant solution?
template <class InputIterator1, class InputIterator2, class InputIterator3,
class OutputIterator, class TrenaryOperation>
OutputIterator transform3(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator3 first3, OutputIterator result,
TrenaryOperation trenary_op)
{
while (first1 != last1) {
*result = trenary_op(*first1, *first2, *first3);
++result; ++first1; ++first2; ++first3;
}
return result;
}
A simple version of this can be achieved to create an n-ary transform like this:
template <class Functor, class OutputIterator,
class Input1, class ... Inputs>
OutputIterator transform(Functor f, OutputIterator out,
Input1 first1, Input1 last1,
Inputs ... inputs)
{
while(first1 != last1)
*out++ = f(*first1++, *inputs++...);
return out;
}
This version tries to stay as close to the existing transform as possible, taking one first/last pair of iterators and the rest are just firsts. This leaves it up to the user to make sure all the ranges are valid, just as with the binary-transform.
As for performance, I agree with ShighShagh's comment about performance not likely being an issue here. The compiler will be in a better place than you to determine what optimizations to take because each instantiation could lead to different situations that the programmer couldn't possibly know while writing this function.
Here is my take at it. I got a little carried away, which resulted in a transform function for N dimensions:
#include <iostream> // for std::cout
#include <iterator> // for std::ostream_iterator
#include <tuple> // for std::tie
#include <type_traits> // for std::enable_if
#include <vector> // for std::vector
template<typename T>
struct identity { using type = T; };
template<typename Integral, Integral... N>
struct integer_sequence {
template<Integral Offset>
struct offset : identity<integer_sequence<Integral, (N + Offset)...>> { };
};
namespace detail {
template<typename... T>
void ignore(T&&...) { }
template<std::size_t Idx, typename... T>
inline auto nth_arg(T&&... arg)
-> decltype(std::get<Idx>(std::tie(arg...))) {
return std::get<Idx>(std::tie(arg...));
}
template<std::size_t N, std::size_t... T>
struct gen_iter_indices
: gen_iter_indices<(N - 2), (N - 2), T...> { };
template<std::size_t... T>
struct gen_iter_indices<0, T...>
: identity<integer_sequence<std::size_t, T...>> { };
template<
typename... Iterator,
typename Integral,
Integral... Begin,
Integral... End
>
inline static bool eq_n(const std::tuple<Iterator...>& iters,
integer_sequence<Integral, Begin...>,
integer_sequence<Integral, End...>)
{
const bool res[] { (std::get<Begin>(iters) == std::get<End>(iters))... };
for(std::size_t i = 0; i < sizeof...(Begin); ++i) {
if(res[i]) { return true; }
}
return false;
}
template<typename... Iterator, typename Integral, Integral... Begin>
inline static void increment_n(const std::tuple<Iterator...>& iters,
integer_sequence<Integral, Begin...>)
{
ignore(++std::get<Begin>(iters)...);
}
template<
typename NaryOperation,
typename... Iterator,
typename Integral,
Integral... Begin
>
inline auto call_n(const std::tuple<Iterator...>& iters,
NaryOperation op,
integer_sequence<Integral, Begin...>)
-> decltype(op(*std::get<Begin>(iters)...))
{
return op(*std::get<Begin>(iters)...);
}
}
template<
typename OutputIter,
typename NaryOperation,
typename... InputIter,
typename = typename std::enable_if<
(2 <= sizeof...(InputIter)) && // Atleast one iterator pair
(0 == (sizeof...(InputIter) % 2)) // and multiple of two
>::type
>
static OutputIter transform_n(OutputIter out_iter,
NaryOperation op,
InputIter... in_iter)
{
using begins = typename detail::gen_iter_indices<sizeof...(InputIter)>::type;
using ends = typename begins::template offset<1>::type;
const auto iters = std::tie(in_iter...); // tuple of references to iterators
while(!detail::eq_n(iters, begins{}, ends{})) {
*out_iter = detail::call_n(iters, op, begins{});
++out_iter;
detail::increment_n(iters, begins{});
}
return out_iter;
}
Usage is simple:
int main(int argc, char** argv) {
std::vector<int> v1 { 1, 2, 3 };
std::vector<int> v2 { 4, 5, 6 };
std::vector<int> v3 { 7, 8, 9 };
std::vector<int> res { };
res.resize(3);
auto end = transform_n(
res.begin(),
[](int _1, int _2, int _3) { return _1 + _2 + _3; },
v1.begin(),
v1.end(),
v2.begin(),
v2.end(),
v3.begin(),
v3.end()
);
std::copy(res.begin(), end, std::ostream_iterator<int>(std::cout, " "));
return 0;
}
Output on ideone.
Note that in this version works with containers or different sizes, so if you know your containers will always be the same size, you can edit detail::eq_n to only check the first begin/end iterators for equality.
Related
Suppose we have this template
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
...
}
How can it be stated that, obviously the elements in container should be of type T?
Can this all be abbreviated (maybe in C++11)?
While other answers using value_type are correct , the canonical solution to this frequent problem is to not pass the container in the first place : use the Standard Library semantics, and pass a pair of iterators.
By passing iterators, you don't have to worry about the container itself. Your code is also much more generic : you can act on ranges, you can use reversed iterators, you can combine your template with other standard algorithms etc.. :
template<typename Iterator, typename T>
bool contains (Iterator begin, Iterator end, const T& value) {
...
}
int main(){
std::vector<int> v { 41, 42 };
contains(std::begin(v), std::end(v), 42);
};
If you want to check the type carried by Iterator, you can use std::iterator_traits :
static_assert(std::is_same<typename std::iterator_traits<Iterator>::value_type, T>::value, "Wrong Type");
(Note that this assertion is generally not needed : if you provide a value not comparable with T, the template will not compile in the first place)
The final template would look like :
template<typename Iterator, typename T>
bool contains (Iterator begin, Iterator end, const T& value) {
static_assert(std::is_same<typename std::iterator_traits<Iterator>::value_type, T>::value, "Wrong Type");
while(begin != end)
if(*begin++ == value)
return true;
return false;
}
Live demo
Notes:
1) This should not be a surprise, but our contains template now has almost the same signature than std::find (which returns an iterator) :
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
2) If modifying the signature of the original contains is too much, you can always forward the call to our new template :
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
return contains(std::begin(theContainer), std::end(theContainer), theReference);
}
You might restrict the container type in the template:
#include <algorithm>
#include <iostream>
#include <vector>
template< template<typename ... > class Container, typename T>
bool contains(const Container<T>& container, const T& value) {
return std::find(container.begin(), container.end(), value) != container.end();
}
int main()
{
std::vector<int> v = { 1, 2, 3 };
std::cout << std::boolalpha
<< contains(v, 0) << '\n'
<< contains(v, 1) << '\n';
// error: no matching function for call to ‘contains(std::vector<int>&, char)’
contains(v, '0') ;
return 0;
}
A more complete solution (addressing some comments):
#include <algorithm>
#include <array>
#include <iostream>
#include <map>
#include <set>
#include <vector>
// has_member
// ==========
namespace Detail {
template <typename Test>
struct has_member
{
template<typename Class>
static typename Test::template result<Class>
test(int);
template<typename Class>
static std::false_type
test(...);
};
}
template <typename Test, typename Class>
using has_member = decltype(Detail::has_member<Test>::template test<Class>(0));
// has_find
// ========
namespace Detail
{
template <typename ...Args>
struct has_find
{
template<
typename Class,
typename R = decltype(std::declval<Class>().find(std::declval<Args>()... ))>
struct result
: std::true_type
{
typedef R type;
};
};
}
template <typename Class, typename ...Args>
using has_find = has_member<Detail::has_find<Args...>, Class>;
// contains
// ========
namespace Detail
{
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(std::false_type, const Container<Key, Args...>& container, const Key& value) {
bool result = std::find(container.begin(), container.end(), value) != container.end();
std::cout << "Algorithm: " << result << '\n';;
return result;
}
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(std::true_type, const Container<Key, Args...>& container, const Key& value) {
bool result = container.find(value) != container.end();
std::cout << " Member: " << result << '\n';
return result;
}
}
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(const Container<Key, Args...>& container, const Key& value) {
return Detail::contains(has_find<Container<Key, Args...>, Key>(), container, value);
}
template<typename T, std::size_t N>
bool contains(const std::array<T, N>& array, const T& value) {
bool result = std::find(array.begin(), array.end(), value) != array.end();
std::cout << " Array: " << result << '\n';;
return result;
}
// test
// ====
int main()
{
std::cout << std::boolalpha;
std::array<int, 3> a = { 1, 2, 3 };
contains(a, 0);
contains(a, 1);
std::vector<int> v = { 1, 2, 3 };
contains(v, 0);
contains(v, 1);
std::set<int> s = { 1, 2, 3 };
contains(s, 0);
contains(s, 1);
std::map<int, int> m = { { 1, 1}, { 2, 2}, { 3, 3} };
contains(m, 0);
contains(m, 1);
return 0;
}
For standard container, you may use value_type:
template<typename Container>
bool contains (const Container & theContainer, const typename Container::value_type& theReference) {
...
}
Note that there is also const_reference in your case:
template<typename Container>
bool contains (const Container & theContainer, typename Container::const_reference theReference) {
...
}
You can check the value_type of container and T using static_assert
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
static_assert( std::is_same<typename Container::value_type, T>::value,
"Invalid container or type" );
// ...
}
Demo Here
Using std::enable_if (http://en.cppreference.com/w/cpp/types/enable_if), but a little more complicated than with static_assert.
EDIT: According to P0W's comment, using std::enable_if allows us to use SFINAE, which is nice when you decide to have more overloads. For example if the compiler decides to use this templated function, with a Container with no value_type typedefed, it won't generate an error instantly, like static_assert would, just looks for other functions which perfectly fits the signature.
Tested on Visual Studio 12.
#include <vector>
#include <iostream>
template<typename Container, typename T>
typename std::enable_if<
std::is_same<T, typename Container::value_type>::value, bool>::type //returns bool
contains(const Container & theContainer, const T & theReference)
{
return (std::find(theContainer.begin(), theContainer.end(), theReference) != theContainer.end());
};
int main()
{
std::vector<int> vec1 { 1, 3 };
int i = 1;
float f = 1.0f;
std::cout << contains(vec1, i) << "\n";
//std::cout << contains(vec1, f); //error
i = 2;
std::cout << contains(vec1, i) << "\n";
};
output:
1
0
PS: Your original function does it too, except that allows derived classes too. These solutions does not.
There is any way to search an item or attribute or variable in C++ using STL .
We can use any container of STL providing Searching time as less as possible . Container contains pair<int,int> . i would like to search a pair p(a,x) which should return all pairs X whose p.first == Xi.first and p.second != Xi.second for all i .
e.g.
Let container is unordered_set .
unordered_set< pair<int , int > > myset =
{{1,2},{1,5},{1,6},{2,4},{3,5},{4,6},{6,7},{6,8}};
if i search for p(1,5) then it should return pair(1,2),(1,6)
if i search for p(2,4) or (3,5),(6,7) then it should return NULL i.e. nothing
if i search for p(6,7) then it should return pair(6,8)
Something along the lines of
std::vector<std::pair<int, int>>
find_nonmatching_values(const std::unordered_multimap<int, int> & thing,
int key, int value) {
std::vector<std::pair<int, int>> ret;
auto range = thing.equal_range(key);
std::copy_if(range.first, range.second, std::back_inserter(ret),
[value](const std::pair<const int, int> &p)
{ return p.second != value; });
return ret;
}
Demo. Templatizing this code is left as an exercise for the reader.
Slightly more general than T.C.s version:
#include <type_traits>
#include <iterator>
template <typename T, typename InputIterator, typename OutputIterator, typename Comparator>
void find_mismatches(InputIterator first, InputIterator last,
T const& val, OutputIterator out, Comparator comp)
{
for (; first != last; ++first)
{
auto&& f = *first;
if (!comp(f.second, val))
*out++ = f;
}
}
template <typename AssociativeCont, typename OutputIterator, typename Comparator>
void find_mismatches(AssociativeCont&& rng, typename std::remove_reference<AssociativeCont>::type::value_type const& val, OutputIterator out, Comparator comp)
{
auto range = rng.equal_range(val.first);
find_mismatches(range.first, range.second, val.second, out, comp);
}
template <typename AssociativeCont, typename OutputIterator>
void find_mismatches(AssociativeCont&& rng, typename std::remove_reference<AssociativeCont>::type::value_type const& val, OutputIterator out)
{
auto range = rng.equal_range(val.first);
find_mismatches(range.first, range.second, val.second, out, std::equal_to<decltype(val.second)>());
}
Demo. Note that you could still expand this by using a template parameter that is a pointer-to-member to a value_type's member.
I am having problems implementing the specialization of a class template that uses template template parameters. For example, I would like to write a class that is used for sorting:
template <template <typename, typename...> class container_type> struct SortTraits {
template <class comparator_type>
static void sort(container_type<size_t> &front, comparator_type comp) {
std::sort(front.begin(), front.end(), comp);
}
};
template <> struct SortTraits<std::list> {
template <class T, class comparator_type>
static void sort(std::list<T> &front, comparator_type comp) {
front.sort(comp);
}
};
Then I would call this as follows:
struct MyFunctor {
bool operator()( const size_t& a, const size_t& b ) const {
return a>b;
}
};
//! Alias template used for the type of container used to store a front
template <class T> using container_template = std::list<T>;
int main(int argc, char *argv[]) {
//! Concrete type for a front
typedef container_template<size_t> front_type;
front_type myContainer = {3,5,2,6,3,6,7};
MyFunctor mySortFunctor;
SortTraits<container_template>::sort(myContainer, mySortFunctor);
for (auto it = myContainer.begin(); it != myContainer.end(); ++it)
cout<<" "<<*it;
cout<<endl;
exit(0);
}
I use the specialization for the list as I would like to call the sort function that the std::list implements. Yet, this code doesn't work. Why the template specialization is not being found?
The error I get is:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/algorithm:3772:40: error: invalid operands
to binary expression ('std::__1::__list_iterator' and 'std::__1::__list_iterator')
difference_type __len = __last - __first;
~~~~~~ ^ ~~~~~~~
And it's because it's not finding the specialization.
Why bother with the traits class and the partial specialization at all instead of simply overloading a sort function? As the saying goes, std::less is more. (Live at Coliru)
template <typename Container>
using less = std::less<
typename std::decay<
decltype(*std::declval<Container&>().begin())
>::type
>;
template<typename Container, typename Compare = less<Container>>
inline void sort(Container& container, Compare&& comp = {}) {
using std::begin;
using std::end;
std::sort(begin(container), end(container), std::forward<Compare>(comp));
}
template<typename... T, typename Compare = less<std::list<T...>>>
inline void sort(std::list<T...>& list, Compare&& comp = {}) {
list.sort(std::forward<Compare>(comp));
}
For that matter, a generic sort function that prefers member sort when it exists would save you the trouble of writing overloads at all (Live at Coliru):
namespace detail {
using std::begin;
using std::end;
template<typename Container, typename Compare>
inline void sort_(Container& container, Compare&& comp, ...) {
std::sort(begin(container), end(container), std::forward<Compare>(comp));
}
template<typename Container, typename Compare>
inline auto sort_(Container& container, Compare&& comp, int) ->
decltype(container.sort(std::forward<Compare>(comp))) {
return container.sort(std::forward<Compare>(comp));
}
template<typename Container, typename Compare = std::less<
typename std::decay<
decltype(*begin(std::declval<Container&>()))
>::type
>>
inline void sort(Container& container, Compare&& comp = {}) {
sort_(container, std::forward<Compare>(comp), 0);
}
} // namespace detail
using detail::sort;
Why not just use type parameters?:
template<typename CONTAINER>
struct SortTraits
{
template<typename COMPARATOR>
static void sort( CONTAINER& container , COMPARATOR comparator = std::less<> )
{
std::sort( std::begin( container ) , std::end( container ) , comparator );
}
};
template<typename T>
struct SortTraits<std::list<T>>
{
template<typename COMPARATOR>
static void sort( std::list<T>& list , COMPARATOR comparator )
{
list.sort( comparator );
}
};
namespace utils
{
template<typename CONTAINER , typename COMPARATOR>
void sort( CONTAINER& container , COMPARATOR comparator )
{
SortTraits<CONTAINER>::sort( container , comparator );
}
}
int main()
{
std::array<int,4> arr = { 1 , 2 , 3 , 4 };
std::list<int> list = { 1 , 2 , 3 , 4 };
std::vector<int> vec = { 1 , 2 , 3 , 4 };
utils::sort( arr );
utils::sort( list );
utils::sort( vec );
}
Saw this question, Best STL transform - like template function for ternary operators, thought it would be cool to make an narry transform for kicks and giggles.
So I threw this together, seems to work...with a caviate. My preincrement method is...odd to me. I couldn't figure out how to do with without unpacking. I expected the syntax to be something like (++args)... (similar to the deference syntax of (*args)...) but that returns an error. Anyone know how to do it?
#include <iostream>
#include <vector>
inline void preincrement(){}
template <typename T,typename... Ts>
inline void preincrement(T& t,Ts&... ts)
{
++t;
preincrement(ts...);
}
template <class OutputIterator, class NArryOperator, class InputIterator, class... InputIterators>
OutputIterator transform_n(OutputIterator out_iter, NArryOperator oper, InputIterator iter1begin, InputIterator iter1end,InputIterators... args)
{
for (; iter1begin != iter1end; ++out_iter,++iter1begin,preincrement(args...))
{
*out_iter = oper(*iter1begin,(*args)...);
}
return out_iter;
}
template <typename T>
struct noop
{
T operator()(const T& val){return val;}
};
template <typename Ot,typename T,typename... Ts>
struct nsum
{
Ot operator()(const T& t,const Ts&... ts)
{
return (Ot)t+nsum<Ot,Ts...>()(ts...);
}
};
template <typename Ot, typename T>
struct nsum<Ot,T>
{
Ot operator()(const T& t)
{
return (Ot)t;
}
};
int main(int argc,char** argv)
{
std::vector<int> rng;
for (int i = 0; i < 10; i++) {rng.push_back(i);}
std::vector<int> rng1 = rng;
std::vector<int> rng2 = rng;
std::vector<int> rng3 = rng;
std::vector<float> out(rng.size());
auto beg = out.begin();
auto end = transform_n(beg,nsum<double,int,int,int,int>(),
rng.begin(),rng.end(),
rng1.begin(),
rng2.begin(),
rng3.begin());
for (auto i = beg; i != end; ++i)
{
std::cout << *i << std::endl;
}
}
A solution using postincrement directly:
template <class OutputIterator, class NArryOperator,
class InputIterator, class... InputIterators>
OutputIterator transform_n(OutputIterator out_iter, NArryOperator oper,
InputIterator iter1begin, InputIterator iter1end,
InputIterators... args)
{
while(iter1begin != iter1end)
{
*out_iter++ = oper(*iter1begin++, (*args++)...);
}
return out_iter;
}
About incrementing in the "increment-expression" of a for-statement:
You can't expand in the "increment-expression" of the for-statement; pack expansion is restricted to a few contexts, such that operator, overloads cannot apply [temp.variadic]/4. The usual tricks are required, such as expanding in a function call (what you used) or using a dummy array:
using iarr = int[];
for (; iter1begin != iter1end;
++out_iter,++iter1begin,(void)iarr{0, (++args,void(),0)...})
Let's say I'd like to write an algorithm that prints the value of each element in a container. The container could be a Sequence or Associative container (e.g. std::vector or std::map). In the case of a sequence, the algorithm would print the value_type. In the case of an associative type, the algorithm would print the data_type. How can I write my algorithm (only once!) so that it works with either one? Pretend that the algorithm is complex and that I don't want to repeat it for both sequence/associative versions.
For example:
template <class Iterator>
void printSequence(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << *it;
}
template <class Iterator>
void printAssociative(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << it->second;
}
template <class Iterator>
void printEither(Iterator begin, Iterator end)
{
// ????
}
The difference that you have between your two function templates is not a difference between associative containers and sequences but a difference in the part of the type that is stored.
To clarify, std::set is an associative container but would work with your printSequence function; the problem with map is not the fact that it is associative, but that the value_type is a pair an you are only interested on the second part.
The simplest thing to do is to abstract the dereferencing operation.
E.g. used like this:
#include <map>
#include <vector>
template< class X, class Y >
void test( const std::map<X, Y>& mp )
{
printEither( mp.begin(), mp.end(), MakeMapDerefence( mp ) );
}
template< class Y >
void test( const std::vector<Y>& vec )
{
printEither( vec.begin(), vec.end(), MakeSimpleDereference( vec ) );
}
Defined like this (there's a fair bit of boiler plate that's probably a boost one-liner):
template< class ReferenceType, class IteratorType >
struct SimpleDereference
{
ReferenceType operator() ( IteratorType i ) const
{
return *i;
}
};
template< class ReferenceType, class IteratorType >
struct MapDereference
{
ReferenceType operator() ( IteratorType i ) const
{
return i->second;
}
};
// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::const_reference
, typename Container::const_iterator >
MakeSimpleDereference( const Container& )
{
return SimpleDereference< typename Container::const_reference
, typename Container::const_iterator >();
}
// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::reference
, typename Container::iterator >
MakeSimpleDereference( Container& )
{
return SimpleDereference< typename Container::reference
, typename Container::iterator >();
}
// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< const typename Container::mapped_type&
, typename Container::const_iterator >
MakeMapDerefence( const Container& )
{
return MapDereference< const typename Container::mapped_type&
, typename Container::const_iterator >();
}
// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< typename Container::mapped_type&
, typename Container::iterator >
MakeMapDereference( Container& )
{
return MapDereference< typename Container::mapped_type&
, typename Container::iterator >();
}
#include <iostream>
#include <ostream>
template <class Iterator, class Dereference> void printEither(Iterator begin, Iterator end, Dereference deref)
{
for (; begin != end; ++begin)
{
std::cout << deref(begin);
}
}
I've whipped up an iterator adapter based on Charles' answer. I'm posting it here in case anyone finds it useful:
#include <iostream>
#include <map>
#include <vector>
#include <boost/iterator/iterator_adaptor.hpp>
//------------------------------------------------------------------------------
template <class Iterator>
void print(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << *it << "\n";
}
//------------------------------------------------------------------------------
template <class BaseIterator>
class MapDataIterator :
public boost::iterator_adaptor<
MapDataIterator<BaseIterator>,
BaseIterator,
typename BaseIterator::value_type::second_type >
{
public:
typedef typename BaseIterator::value_type::second_type& reference;
MapDataIterator() {}
explicit MapDataIterator(BaseIterator base)
: MapDataIterator::iterator_adaptor_(base) {}
private:
friend class boost::iterator_core_access;
reference dereference() const
{return this->base_reference()->second;}
};
//------------------------------------------------------------------------------
int main()
{
std::vector<int> vec;
vec.push_back(31);
vec.push_back(41);
std::map<int,int> map;
map[31] = 41;
map[59] = 26;
typedef MapDataIterator< std::map<int,int>::iterator > DataIter;
print( vec.begin(), vec.end() );
print( DataIter(map.begin()), DataIter(map.end()) );
}
This solution has the added advantage that the algorithm need not be aware of how to dereference the iterators. It is also reusable for any existing algorithm that expects a "data sequence".
I'm surprised this little critter doesn't already exist in Boost.