Implementing partial template specialization of template template parameter - c++

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 );
}

Related

SFINAE: 'enable_if' cannot be used to disable this declaration

I want to enable and disable a function declaration in a template class, just based on if the template parameter has one type defined or not for which I use boost/tti/has_type.hpp. However, I got the complains from compiler, i.e., 'enable_if' cannot be used to disable this declaration.
#include <boost/tti/has_type.hpp>
#include <iostream>
#include <vector>
#include <set>
using namespace std;
BOOST_TTI_HAS_TYPE(key_type)
template <typename container>
class adapter : public container
{
public:
using container::container;
public:
template <typename type = typename enable_if<!has_type_key_type<container>::value,typename container::value_type>::type>
bool contains(typename container::value_type const & v) { return find(begin(*this),end(*this),v) != end(*this); }
template <typename type = typename enable_if<has_type_key_type<container>::value,typename container::value_type>::type>
bool contains(typename container::key_type const & k) { return this->find(k) != this->end(); }
};
int main()
{
cout << has_type_key_type<adapter<vector<int>>>::value << endl;
cout << has_type_key_type<adapter<set<int>>>::value << endl;
}
How could I resolve it? However, if I change it to similar non-member function template, it works.
#include <boost/tti/has_type.hpp>
#include <iostream>
#include <vector>
#include <set>
using namespace std;
BOOST_TTI_HAS_TYPE(key_type)
template <typename container, typename enable_if<!has_type_key_type<container>::value,int>::type = 0>
bool contains(container const & c, typename container::value_type const & v) { return find(begin(c),end(c),v) != end(c); }
template <typename container, typename enable_if<has_type_key_type<container>::value,int>::type = 0>
bool contains(container const & c, typename container::key_type const & k) { return c.find(k) != c.end(); }
template <typename container>
class adapter : public container
{
public:
using container::container;
public:
// ...
};
int main()
{
vector<double> v{3.14};
set<double> s{2.71};
cout << contains(v,3.14) << endl;
cout << contains(s,2.71) << endl;
}
if I change it to similar non-member function template, it works.
The point is: funtion template.
Your code doesn't works because SFINAE works over templates, with test related to the template parameter. Your contains() method is a function, inside a template class, but isn't a template function.
To make SFINAE works for contains(), you have to transform it in a template function.
You have seen that works outside the class, but works also inside the class.
For example, with the following trick (caution: code not tested)
// .......VVVVVVVVVVVVVVVVVVVVVV
template <typename C = container, // ................V
typename std::enable_if<!has_type_key_type<C>::value, int>::type = 0>
bool contains (typename container::value_type const & v)
{ return find(begin(*this),end(*this),v) != end(*this); }
// .......VVVVVVVVVVVVVVVVVVVVVV
template <typename C = container, // ...............V
typename std::enable_if<has_type_key_type<C>::value, int>::type = 0>
bool contains (typename container::key_type const & k)
{ return this->find(k) != this->end(); }
Observe that the SFINAE test (has_type_key_type<C>::value) now involve C, the function's template parameter, not container, the template parameter of the class.
If you want avoid that constains() can be "hijacked" (explicitly setting a type for C, different from container, you can add a variadic non-type (and unused) template parameter.
For example
// .......VVVVVV
template <int..., typename C = container,
typename std::enable_if<!has_type_key_type<C>::value, int>::type = 0>
bool contains (typename container::value_type const & v)
{ return find(begin(*this),end(*this),v) != end(*this); }
// .......VVVVVV
template <int..., typename C = container,
typename std::enable_if<has_type_key_type<C>::value, int>::type = 0>
bool contains (typename container::key_type const & k)
{ return this->find(k) != this->end(); }
Off Topic: you you can use at least C++14, you ca use std::enable_if_t, so
std::enable_if_t<has_type_key_type<C>::value, int> = 0
instead of
typename std::enable_if<has_type_key_type<C>::value, int>::type = 0

Template function to extract value out of several nested unordered_map's

Let's assume I have a nested std::unordered_map that looks like this:
std::unordered_map<ResourceName, std::unordered_map<HAL::ResourceFormat::Color, HAL::RTDescriptor>>
I want a function that will return a pointer to HAL::RTDescriptor based on two keys ResourceName and HAL::ResourceFormat::Color if object is present or nullptr otherwise. The straightforward implementation looks like this:
const HAL::RTDescriptor* ResourceDescriptorStorage::GetRTDescriptor(ResourceName resourceName, HAL::ResourceFormat::Color format) const
{
auto mapIt = mRTDescriptorMap.find(resourceName);
if (mapIt == mRTDescriptorMap.end()) {
return nullptr;
}
auto& nestedMap = mapIt->second;
auto nestedMapIt = nestedMap.find(format);
if (nestedMapIt == nestedMap.end()) {
return nullptr;
}
return &nestedMapIt->second;
}
Is there a way to use templates to generalize the logic?
Something with parameter packs for keys. Something that will go through each nested container, check for object availability and return it or nullptr at the end:
template<
template<class...> class AssociativeContainer,
class... Keys
>
decltype(auto) Find(const AssociativeContainer<...>& rootContainer, Keys&&... keys)
{
...
}
Simpler solution (requires C++17):
template<class AssociativeContainer, class Key, class... Keys>
auto Find(const AssociativeContainer& container, Key&& key, Keys&&... keys){
auto it = container.find(std::forward<Key>(key));
bool found = it != container.end();
if constexpr(sizeof...(Keys) == 0)
return found ? &it->second : nullptr;
else
return found ? Find(it->second, std::forward<Keys>(keys)...) : nullptr;
}
This also allows to get a reference to any inbetween container, as it doesn't require to pass all keys.
Is there a way to use templates to generalize the logic? Something with parameter packs for keys. Something that will go through each nested container, check for object availability and return it or nullptr at the end:
Require a little of work (maybe someone more expert than me can make it simpler) but sure it's possible.
By example... given a custom type traits (and using to simplify the use) as follows
template <typename T>
struct lastType
{ using type = T; };
template <template <typename...> class C, typename K, typename V>
struct lastType<C<K, V>> : public lastType<V>
{ };
template <typename T>
using lastType_t = typename lastType<T>::type;
you can write Find() recursively as follows
// ground case
template <typename V>
V const * Find (V const & val)
{ return &val; }
// recursion case
template <typename C, typename K0, typename ... Ks>
lastType_t<C> const * Find (C const & cnt, K0 && key0, Ks && ... keys)
{
auto mapIt = cnt.find(std::forward<K0>(key0));
if ( mapIt == cnt.cend() )
return nullptr;
return Find(mapIt->second, std::forward<Ks>(keys)...);
}
The following is a full compiling example
#include <map>
#include <string>
#include <iostream>
#include <unordered_map>
template <typename T>
struct lastType
{ using type = T; };
template <template <typename...> class C, typename K, typename V>
struct lastType<C<K, V>> : public lastType<V>
{ };
template <typename T>
using lastType_t = typename lastType<T>::type;
template <typename V>
V const * Find (V const & val)
{ return &val; }
template <typename C, typename K0, typename ... Ks>
lastType_t<C> const * Find (C const & cnt, K0 && key0, Ks && ... keys)
{
auto mapIt = cnt.find(std::forward<K0>(key0));
if ( mapIt == cnt.cend() )
return nullptr;
return Find(mapIt->second, std::forward<Ks>(keys)...);
}
using typeC = std::map<int,
std::unordered_map<std::string,
std::unordered_map<long,
std::map<char, long long>>>>;
int main ()
{
typeC c;
c[0]["one"][2l]['3'] = 4ll;
auto v = Find(c, 0, "one", 2l, '3');
std::cout << (*v) << std::endl;
static_assert( std::is_same_v<decltype(v), long long const *>, "!" );
}
-- EDIT --
I'm particularly dumb today: as highlighted by krisz in his answer (thanks), the ternary operator permit the use of auto as return type (from C++14).
So there is no needs of the lastType custom type traits and Find() can be simply written as
// ground case
template <typename V>
V const * Find (V const & val)
{ return &val; }
// recursion case
template <typename C, typename K0, typename ... Ks>
auto Find (C const & cnt, K0 && key0, Ks && ... keys)
{
auto mapIt = cnt.find(std::forward<K0>(key0));
return mapIt == cnt.cend()
? nullptr
: Find(mapIt->second, std::forward<Ks>(keys)...);
}
For C++11, the recursion case require also the trailing return type; by example
-> decltype(Find(cnt.find(std::forward<K0>(key0))->second, std::forward<Ks>(keys)...))

Variadic argument subset

I am trying to obtain a subset of the variadic arguments of current class wrapper to instantiate a new one
Currently I have this:
// Reference: https://stackoverflow.com/questions/27941661/generating-one-class-member-per-variadic-template-argument
// Template specialization
template<typename T, typename... Next> class VariadicClass;
// Base case extension
template <typename T>
class VariadicClass<T> {
private:
T value_;
protected:
void SetField(T & value) {
value_ = value;
}
T & GetField() {
return value_;
}
};
// Inductive case
template <typename T, typename ... Next>
class VariadicClass : public VariadicClass<T>, public VariadicClass<Next...> {
public:
// Copy the values into the variadic class
template <typename F>
void Set(F f) {
this->VariadicClass<F>::SetField(f);
}
// Retrieve by reference
template <typename F>
F & Get() {
return this->VariadicClass<F>::GetField();
}
};
And what I want to achieve is something along the following:
[C]: A subset of Args...
VariadicClass<[C]> * Filter(VariadicClass<Args...> input) {
return new VariadicClass<[C]>(GetSubsetFrom(input, [C]));
}
VariadicClass<int, bool, char> class1;
VariadicClass<int, bool> * variadic = Filter(class1);
You can assume that each type is only once in the variadic class and that I will always ask for a subset of the current variadic types. I don't know if this is currently possible in C++ 11?
Thank you for your help.
It seems to me that you're trying to reinvent the wheel (where "wheel", in this case, is std::tuple).
Anyway, what you ask seems simple to me
template <typename ... As1, typename ... As2>
VariadicClass<As1...> * Filter(VariadicClass<As2...> in)
{
using unused = int[];
auto ret = new VariadicClass<As1...>();
(void)unused { 0, (ret->template Set<As1>(in.template Get<As1>()), 0)... };
return ret;
}
The problem I see is that the As1... types (the types of the returned VariadicClass) aren't deducible by the returned value, so you can't write
VariadicClass<int, bool> * variadic = Filter(class1);
You have to explicit the As1... types calling Filter(), so
VariadicClass<int, bool> * variadic = Filter<int, bool>(class1);
or, maybe better,
auto variadic = Filter<int, bool>(class1);
The following is a full compiling example
#include <iostream>
template <typename, typename...>
class VariadicClass;
template <typename T>
class VariadicClass<T>
{
private:
T value_;
protected:
void SetField (T & value)
{ value_ = value; }
T & GetField ()
{ return value_; }
};
template <typename T, typename ... Next>
class VariadicClass : public VariadicClass<T>, public VariadicClass<Next...>
{
public:
template <typename F>
void Set (F f)
{ this->VariadicClass<F>::SetField(f); }
template <typename F>
F & Get()
{ return this->VariadicClass<F>::GetField(); }
};
template <typename ... As1, typename ... As2>
VariadicClass<As1...> * Filter(VariadicClass<As2...> in)
{
using unused = int[];
auto ret = new VariadicClass<As1...>();
(void)unused { 0, (ret->template Set<As1>(in.template Get<As1>()), 0)... };
return ret;
}
int main()
{
VariadicClass<int, bool, char> c1;
c1.Set<int>(42);
c1.Set<bool>(true);
c1.Set<char>('Z');
auto pC2 = Filter<int, bool>(c1);
std::cout << pC2->Get<int>() << std::endl;
std::cout << pC2->Get<bool>() << std::endl;
delete pC2;
}
Off Topic Unrequested Suggestion: you're using C++11 so... try to avoid the direct use of pointer and try to use smart pointers (std::unique_ptr, std::shared_ptr, etc.) instead.
First of all I think you shouldn't write your own variadic class as we already have std::tuplein place.
I wonder that you sit on c++11because it is quite old. Even c++14is outdated but if you can switch, the solution is very simple:
template < typename DATA, typename FILTER, std::size_t... Is>
auto Subset_Impl( const DATA& data, FILTER& filter, std::index_sequence<Is...> )
{
filter = { std::get< typename std::remove_reference<decltype( std::get< Is >( filter ))>::type>( data )... };
}
template < typename DATA, typename FILTER, typename IDC = std::make_index_sequence<std::tuple_size<FILTER>::value >>
auto Subset( const DATA& data, FILTER& filter )
{
return Subset_Impl( data, filter, IDC{} );
}
int main()
{
std::tuple< int, float, std::string, char > data { 1, 2.2, "Hallo", 'c' };
std::tuple< float, char > filter;
Subset( data, filter );
std::cout << std::get<0>( filter ) << " " << std::get<1>( filter ) << std::endl;
}
If you really want sit on outdated standards, you can easily implement the missing parts from the standard library your self. One related question is answered here: get part of std::tuple
How the helper templates are defined can also be seen on: https://en.cppreference.com/w/cpp/utility/integer_sequence

How to indicate the type of a container template argument?

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.

Iterate through STL sequence and associative containers using same code?

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.