I admit I had difficulties coming up with a reasonable description for this. I cannot think of a good term that would describe precisely what I'm looking for. Perhaps this could be called a slicing iterator.
Let's say I have something like this:
struct S
{
int i;
char *s;
float f;
};
std::vector<S> v(10);
What I'm looking for is a way to construct an iterator, that would point to a member of S. I'd like to be able to pass it to something like std::min_element without creating a predicate in each case. Something that might look like this:
std::min_element(slicing_iterator(v.begin(), S::f), slicing_iterator(v.end(), S::f));
Is there any template trick that I could use to achieve this? Or perhaps it's already done somewhere in Boost or some other library?
If you're looking for an iterator that converts S into its S::f, this could certainly be done using boost (what can't be?):
std::cout << *std::min_element(
boost::make_transform_iterator(v.begin(), boost::bind(&S::f, _1)),
boost::make_transform_iterator(v.end(), boost::bind(&S::f, _1))
) << '\n';
test: https://ideone.com/jgcHr
But if you're looking for the S whose S::f is the smallest in the vector, the predicate is the most reasonable approach.
If you don't want to create a predicate function for each case, I would suggest not to look for a slicing operator, but to implement your predicate as a lambda function (either using Boost or C++0x). Here you will find a detailed explanation
http://www.codeproject.com/KB/cpp/Sort.aspx
(this is about std::sort, but the comparison in std::min_element works equally.)
Will something like this do the job?
#include <algorithm>
#include <iostream>
#include <vector>
struct S
{
int i;
float f;
S() : i(0), f(0.0f) {}
S(int i_, float f_) : i(i_), f(f_) {}
};
template <typename Iterator, typename T, typename M>
class SlicingIterator : public std::iterator<typename Iterator::iterator_category,M>
{
private:
Iterator m_it;
M T::*m_m;
public:
SlicingIterator(const Iterator& it, M T::*m)
: m_it(it), m_m(m)
{}
const M operator*() const
{
return (*m_it).*m_m;
}
bool operator!=(const SlicingIterator& rhs) const
{
return m_it != rhs.m_it;
}
SlicingIterator& operator++()
{
++m_it;
return *this;
}
bool operator<(const SlicingIterator& rhs) const
{
return m_it < rhs.m_it;
}
};
template <typename Iterator, typename T, typename M>
SlicingIterator<Iterator,T,M> slicing_iterator(const Iterator& it, M T::*m)
{
return SlicingIterator<Iterator,T,M>(it, m);
}
int main()
{
std::vector<S> vec;
vec.push_back(S(23,9));
vec.push_back(S(17,10));
std::copy(slicing_iterator(vec.begin(), &S::f), slicing_iterator(vec.end(), &S::f), std::ostream_iterator<float>(std::cout, " "));
return 0;
}
In addition to what is already suggested you may do it almost exactly like your code sample does.
Example:
template< class IterT, class ObjT, class MemberT >
class slicing_iterator;
template< class IterT, class ObjT, class MemberT >
inline bool operator==(
const slicing_iterator<IterT,ObjT,MemberT>& a,
const slicing_iterator<IterT,ObjT,MemberT>& b
);
template< class IterT, class ObjT, class MemberT >
inline bool operator!=(
const slicing_iterator<IterT,ObjT,MemberT>& a,
const slicing_iterator<IterT,ObjT,MemberT>& b
);
template< class IterT, class ObjT, class MemberT >
class slicing_iterator
{
IterT m_iter;
MemberT ObjT::* m_member;
public:
slicing_iterator( IterT iter, MemberT ObjT::*member ) :
m_iter(iter), m_member(member)
{
}
slicing_iterator& operator++() { ++m_iter; return *this; }
slicing_iterator& operator--() { --m_iter; return *this; }
MemberT& operator*() { return static_cast<ObjT&>(*m_iter).*m_member; }
const MemberT& operator*() const { return static_cast<const ObjT&>(*m_iter).*m_member; }
MemberT* operator->() { return &m_iter->*m_member; }
const MemberT* operator->() const { return &m_iter->*m_member; }
private:
friend bool operator== <IterT,ObjT,MemberT>(
const slicing_iterator<IterT,ObjT,MemberT>& a,
const slicing_iterator<IterT,ObjT,MemberT>& b
);
friend bool operator!= <IterT,ObjT,MemberT>(
const slicing_iterator<IterT,ObjT,MemberT>& a,
const slicing_iterator<IterT,ObjT,MemberT>& b
);
};
template< class IterT, class ObjT, class MemberT >
inline bool operator==(
const slicing_iterator<IterT,ObjT,MemberT>& a,
const slicing_iterator<IterT,ObjT,MemberT>& b
)
{
return a.m_iter == b.m_iter && a.m_member == a.m_member;
}
template< class IterT, class ObjT, class MemberT >
inline bool operator!=(
const slicing_iterator<IterT,ObjT,MemberT>& a,
const slicing_iterator<IterT,ObjT,MemberT>& b
)
{
return a.m_iter != b.m_iter || a.m_member != a.m_member;
}
template< class IterT, class ObjT, class MemberT >
inline slicing_iterator<IterT,ObjT,MemberT>
make_slicing_iterator( IterT iter, MemberT ObjT::*member )
{
return slicing_iterator<IterT,ObjT,MemberT>( iter, member );
}
struct S
{
int i;
char *s;
float f;
};
int main(void)
{
std::vector<S> v(10);
std::min_element(
make_slicing_iterator(v.begin(), &S::f),
make_slicing_iterator(v.end(), &S::f)
);
return 0;
}
At first I didn't notice - it looks similar to what #Stuart Golodetz suggested but the advantage is that operator< doesn't have to be defined for iterator type (e.g. std::list::iterator). It makes this implementation universal.
Related
Problem Description and Question
I have a template class Class1. It contains in map in which I want to insert structures A or B.
The problem is that the structures A and B have different types of member variables. Structure A has an std::string member variable whereas structure B has an int member variable.
The comparator is based on structure A. So obviously when I want to insert a structure B it will not compile.
Class1<B,B> c2;
c2.AddElement({1},{1});
How can I fix that design Issue? For instance is it possible to keep Class1 as template class and do something to TestCompare?
I also have a constraint. I cannot modify the structures A and B. they are written in C code. I have no right to change them because they are external codes used by other users. I just simplified the code as much as possible.
Source Code
The code was compiled on cpp.sh
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;
template<typename T1, typename T2> class Class1 {
public :
struct TestCompare {
bool operator()(const T1 & lhs, const T1 & rhs) const {
return lhs.a < rhs.a;
}
};
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 & value) {
m.emplace(key, value);
}
private :
std::map<T1,T2,TestCompare> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});
// Problem here. Obviously it will not compile because the Operator is using
// the member variable of struct A.
//Class1<B,B> c2;
//c2.AddElement({1},{1});
//return 0;
}
New Source code
// Example program
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;
bool operator<(const A & lhs, const A & rhs) {
return lhs.a < rhs.a;
}
bool operator<(const B & lhs, const B & rhs) {
return lhs.b < rhs.b;
}
template<typename T1, typename T2> class Class1 {
public :
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 value) {
m.emplace(key, value);
}
std::map<T1,T2> getMap() {
return m;
}
private :
std::map<T1,T2> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});
// Problem here. Obviously it will not compile because the Operator is using
// the member variable of struct A.
Class1<B,B> c2;
c2.AddElement({1},{1});
c2.AddElement({2},{2});
for(const auto &e: c2.getMap()) {
std::cout << e.first.b << " " << e.first.b << std::endl;
}
return 0;
}
I guess you could remove TestCompare from Class1 and template that.
template<typename T> struct TestCompare {
bool operator()(const T & lhs, const T & rhs) const {
// default implementation
return lhs < rhs;
}
};
template<typename T1, typename T2> class Class1 {
...
private :
std::map<T1,T2,TestCompare<T1>> m;
}
You could then specialise TestCompare for A and B
template<> struct TestCompare<A> {
bool operator()(const A & lhs, const A & rhs) const {
return lhs.a < rhs.a;
}
};
template<> struct TestCompare<B> {
bool operator()(const B & lhs, const B & rhs) const {
return lhs.b < rhs.b;
}
};
EDIT:
Actually you could just use std::less instead of TestCompare. It amounts to pretty much the same thing, and std::map uses std::less by default.
TestCompare requires that every type you use must have a member a that can be compared using <. That's a lot of requirements, which implies a terrible design. Add a 3rd template parameter that will be used to pass a function or a functor that compares the objects
struct CompareA {
bool operator()(A const & lhs, A const & rhs) const {
return lhs.a < rhs.a;
}
};
struct CompareB {
bool operator()(B const& lhs, B const& rhs) const {
/*...*/
}
};
template<typename KeyT, typename ValueT, typename Compare> class Dict {
public :
Class1() {}
~Class1() {}
void AddElement(KeyT const & key, ValueT const & value) {
m.emplace(key, value);
}
private :
std::map<KeyT, ValueT, Compare> m;
};
Dict<A, B, CompareA> dictA;
Dict<B, B CompareB> dictB;
You could specialize the struct TestCompare, like john has suggested in his answer, and provide it as the default template argument
template<typename KeyT, typename ValueT, typename Compare = TestCompare<KeyT>> class Dict { /*...*/ };
Such solution will allow you to provide only 2 arguments, like so
Dict<B, B> dict;
while still maintaining the ability to provide another comparer if necessary.
I know how to sort a vector of pairs, but how do you sort a pair of vectors? I can think of writing a custom "virtual" iterator over a pair of vectors and sorting that, but that seems quite complex. Is there an easier way? Is there one in C++03? I would like to use std::sort.
This problem arises when processing some data generated in hardware, where a pair of arrays makes more sense than array of pairs (since then there would be all kinds of stride and alignment problems). I realize that otherwise keeping a pair of vector instead of a vector of pairs would be a design flaw (the structure of arrays problem). I'm looking for a fast solution, copying the data to a vector of pairs and then back (I will return it to the HW to do more processing) is not an option.
Example:
keys = {5, 2, 3, 1, 4}
values = {a, b, d, e, c}
and after sorting (by the first vector):
keys = {1, 2, 3, 4, 5}
values = {e, b, d, c, a}
I refer to a "pair of vectors" as the pair of keys and values (stored as e.g. std::pair<std::vector<size_t>, std::vector<double> >). The vectors have the same length.
Let's make a sort/permute iterator, so that we can just say:
int keys[] = { 5, 2, 3, 1, 4 };
char vals[] = { 'a', 'b', 'd', 'e', 'c' };
std::sort(make_dual_iter(begin(keys), begin(vals)),
make_dual_iter(end(keys), end(vals)));
// output
std::copy(begin(keys), end(keys), std::ostream_iterator<int> (std::cout << "\nKeys:\t", "\t"));
std::copy(begin(vals), end(vals), std::ostream_iterator<char>(std::cout << "\nValues:\t", "\t"));
See it Live On Coliru, printing
Keys: 1 2 3 4 5
Values: e b d c a
Based on the idea here, I've implemented this:
namespace detail {
template <class KI, class VI> struct helper {
using value_type = boost::tuple<typename std::iterator_traits<KI>::value_type, typename std::iterator_traits<VI>::value_type>;
using ref_type = boost::tuple<typename std::iterator_traits<KI>::reference, typename std::iterator_traits<VI>::reference>;
using difference_type = typename std::iterator_traits<KI>::difference_type;
};
}
template <typename KI, typename VI, typename H = typename detail::helper<KI, VI> >
class dual_iter : public boost::iterator_facade<dual_iter<KI, VI>, // CRTP
typename H::value_type, std::random_access_iterator_tag, typename H::ref_type, typename H::difference_type>
{
public:
dual_iter() = default;
dual_iter(KI ki, VI vi) : _ki(ki), _vi(vi) { }
KI _ki;
VI _vi;
private:
friend class boost::iterator_core_access;
void increment() { ++_ki; ++_vi; }
void decrement() { --_ki; --_vi; }
bool equal(dual_iter const& other) const { return (_ki == other._ki); }
typename detail::helper<KI, VI>::ref_type dereference() const {
return (typename detail::helper<KI, VI>::ref_type(*_ki, *_vi));
}
void advance(typename H::difference_type n) { _ki += n; _vi += n; }
typename H::difference_type distance_to(dual_iter const& other) const { return ( other._ki - _ki); }
};
Now the factory function is simply:
template <class KI, class VI>
dual_iter<KI, VI> make_dual_iter(KI ki, VI vi) { return {ki, vi}; }
Note I've been a little lazy by using boost/tuples/tuple_comparison.hpp for the sorting. This could pose a problem with stable sort when multiple key values share the same value. However, in this case it's hard to define what is "stable" sort anyways, so I didn't think it important for now.
FULL LISTING
Live On Coliru
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/tuple/tuple_comparison.hpp>
namespace boost { namespace tuples {
// MSVC might not require this
template <typename T, typename U>
inline void swap(boost::tuple<T&, U&> a, boost::tuple<T&, U&> b) noexcept {
using std::swap;
swap(boost::get<0>(a), boost::get<0>(b));
swap(boost::get<1>(a), boost::get<1>(b));
}
} }
namespace detail {
template <class KI, class VI> struct helper {
using value_type = boost::tuple<typename std::iterator_traits<KI>::value_type, typename std::iterator_traits<VI>::value_type>;
using ref_type = boost::tuple<typename std::iterator_traits<KI>::reference, typename std::iterator_traits<VI>::reference>;
using difference_type = typename std::iterator_traits<KI>::difference_type;
};
}
template <typename KI, typename VI, typename H = typename detail::helper<KI, VI> >
class dual_iter : public boost::iterator_facade<dual_iter<KI, VI>, // CRTP
typename H::value_type, std::random_access_iterator_tag, typename H::ref_type, typename H::difference_type>
{
public:
dual_iter() = default;
dual_iter(KI ki, VI vi) : _ki(ki), _vi(vi) { }
KI _ki;
VI _vi;
private:
friend class boost::iterator_core_access;
void increment() { ++_ki; ++_vi; }
void decrement() { --_ki; --_vi; }
bool equal(dual_iter const& other) const { return (_ki == other._ki); }
typename detail::helper<KI, VI>::ref_type dereference() const {
return (typename detail::helper<KI, VI>::ref_type(*_ki, *_vi));
}
void advance(typename H::difference_type n) { _ki += n; _vi += n; }
typename H::difference_type distance_to(dual_iter const& other) const { return ( other._ki - _ki); }
};
template <class KI, class VI>
dual_iter<KI, VI> make_dual_iter(KI ki, VI vi) { return {ki, vi}; }
#include <iostream>
using std::begin;
using std::end;
int main()
{
int keys[] = { 5, 2, 3, 1, 4 };
char vals[] = { 'a', 'b', 'd', 'e', 'c' };
std::sort(make_dual_iter(begin(keys), begin(vals)),
make_dual_iter(end(keys), end(vals)));
std::copy(begin(keys), end(keys), std::ostream_iterator<int> (std::cout << "\nKeys:\t", "\t"));
std::copy(begin(vals), end(vals), std::ostream_iterator<char>(std::cout << "\nValues:\t", "\t"));
}
Just for comparison, this is how much code the split iterator approach requires:
template <class V0, class V1>
class CRefPair { // overrides copy semantics of std::pair
protected:
V0 &m_v0;
V1 &m_v1;
public:
CRefPair(V0 &v0, V1 &v1)
:m_v0(v0), m_v1(v1)
{}
void swap(CRefPair &other)
{
std::swap(m_v0, other.m_v0);
std::swap(m_v1, other.m_v1);
}
operator std::pair<V0, V1>() const // both g++ and msvc sort requires this (to get a pivot)
{
return std::pair<V0, V1>(m_v0, m_v1);
}
CRefPair &operator =(std::pair<V0, V1> v) // both g++ and msvc sort requires this (for insertion sort)
{
m_v0 = v.first;
m_v1 = v.second;
return *this;
}
CRefPair &operator =(const CRefPair &other) // required by g++ (for _GLIBCXX_MOVE)
{
m_v0 = other.m_v0;
m_v1 = other.m_v1;
return *this;
}
};
template <class V0, class V1>
inline bool operator <(std::pair<V0, V1> a, CRefPair<V0, V1> b) // required by both g++ and msvc
{
return a < std::pair<V0, V1>(b); // default pairwise lexicographical comparison
}
template <class V0, class V1>
inline bool operator <(CRefPair<V0, V1> a, std::pair<V0, V1> b) // required by both g++ and msvc
{
return std::pair<V0, V1>(a) < b; // default pairwise lexicographical comparison
}
template <class V0, class V1>
inline bool operator <(CRefPair<V0, V1> a, CRefPair<V0, V1> b) // required by both g++ and msvc
{
return std::pair<V0, V1>(a) < std::pair<V0, V1>(b); // default pairwise lexicographical comparison
}
namespace std {
template <class V0, class V1>
inline void swap(CRefPair<V0, V1> &a, CRefPair<V0, V1> &b)
{
a.swap(b);
}
} // ~std
template <class It0, class It1>
class CPairIterator : public std::random_access_iterator_tag {
public:
typedef typename std::iterator_traits<It0>::value_type value_type0;
typedef typename std::iterator_traits<It1>::value_type value_type1;
typedef std::pair<value_type0, value_type1> value_type;
typedef typename std::iterator_traits<It0>::difference_type difference_type;
typedef /*typename std::iterator_traits<It0>::distance_type*/difference_type distance_type; // no distance_type in g++, only in msvc
typedef typename std::iterator_traits<It0>::iterator_category iterator_category;
typedef CRefPair<value_type0, value_type1> reference;
typedef reference *pointer; // not so sure about this, probably can't be implemented in a meaningful way, won't be able to overload ->
// keep the iterator traits happy
protected:
It0 m_it0;
It1 m_it1;
public:
CPairIterator(const CPairIterator &r_other)
:m_it0(r_other.m_it0), m_it1(r_other.m_it1)
{}
CPairIterator(It0 it0 = It0(), It1 it1 = It1())
:m_it0(it0), m_it1(it1)
{}
reference operator *()
{
return reference(*m_it0, *m_it1);
}
value_type operator *() const
{
return value_type(*m_it0, *m_it1);
}
difference_type operator -(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
// the iterators always need to have the same position
// (incomplete check but the best we can do without having also begin / end in either vector)
return m_it0 - other.m_it0;
}
bool operator ==(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
return m_it0 == other.m_it0;
}
bool operator !=(const CPairIterator &other) const
{
return !(*this == other);
}
bool operator <(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
return m_it0 < other.m_it0;
}
bool operator >=(const CPairIterator &other) const
{
return !(*this < other);
}
bool operator <=(const CPairIterator &other) const
{
return !(other < *this);
}
bool operator >(const CPairIterator &other) const
{
return other < *this;
}
CPairIterator operator +(distance_type d) const
{
return CPairIterator(m_it0 + d, m_it1 + d);
}
CPairIterator operator -(distance_type d) const
{
return *this + -d;
}
CPairIterator &operator +=(distance_type d)
{
return *this = *this + d;
}
CPairIterator &operator -=(distance_type d)
{
return *this = *this + -d;
}
CPairIterator &operator ++()
{
return *this += 1;
}
CPairIterator &operator --()
{
return *this += -1;
}
CPairIterator operator ++(int) // msvc sort actually needs this, g++ does not
{
CPairIterator old = *this;
++ (*this);
return old;
}
CPairIterator operator --(int)
{
CPairIterator old = *this;
-- (*this);
return old;
}
};
template <class It0, class It1>
inline CPairIterator<It0, It1> make_pair_iterator(It0 it0, It1 it1)
{
return CPairIterator<It0, It1>(it0, it1);
}
It is kind of rough around the edges, maybe I'm just bad at overloading the comparisons, but the amount of differences needed to support different implementations of std::sort makes me think the hackish solution might actually be more portable. But the sorting is much nicer:
struct CompareByFirst {
bool operator ()(std::pair<size_t, char> a, std::pair<size_t, char> b) const
{
return a.first < b.first;
}
};
std::vector<char> vv; // filled by values
std::vector<size_t> kv; // filled by keys
std::sort(make_pair_iterator(kv.begin(), vv.begin()),
make_pair_iterator(kv.end(), vv.end()), CompareByFirst());
// nice
And of course it gives the correct result.
Inspired by a comment by Mark Ransom, this is a horrible hack, and an example of how not to do it. I only wrote it for amusement and because I was wondering how complicated would it get. This is not an answer to my question, I will not use this. I just wanted to share a bizarre idea. Please, do not downvote.
Actually, ignoring multithreading, I believe this could be done:
template <class KeyType, class ValueVectorType>
struct MyKeyWrapper { // all is public to save getters
KeyType k;
bool operator <(const MyKeyWrapper &other) const { return k < other.k; }
};
template <class KeyType, class ValueVectorType>
struct ValueVectorSingleton { // all is public to save getters, but kv and vv should be only accessible by getters
static std::vector<MyKeyWrapper<KeyType, ValueVectorType> > *kv;
static ValueVectorType *vv;
static void StartSort(std::vector<MyKeyWrapper<KeyType, ValueVectorType> > &_kv, ValueVectorType &_vv)
{
assert(!kv && !vv); // can't sort two at once (if multithreading)
assert(_kv.size() == _vv.size());
kv = &_kv, vv = &_vv; // not an attempt of an atomic operation
}
static void EndSort()
{
kv = 0, vv = 0; // not an attempt of an atomic operation
}
};
template <class KeyType, class ValueVectorType>
std::vector<MyKeyWrapper<KeyType, ValueVectorType> >
*ValueVectorSingleton<KeyType, ValueVectorType>::kv = 0;
template <class KeyType, class ValueVectorType>
ValueVectorType *ValueVectorSingleton<KeyType, ValueVectorType>::vv = 0;
namespace std {
template <class KeyType, class ValueVectorType>
void swap(MyKeyWrapper<KeyType, ValueVectorType> &a,
MyKeyWrapper<KeyType, ValueVectorType> &b)
{
assert((ValueVectorSingleton<KeyType, ValueVectorType>::vv &&
ValueVectorSingleton<KeyType, ValueVectorType>::kv)); // if this triggers, someone forgot to call StartSort()
ValueVectorType &vv = *ValueVectorSingleton<KeyType, ValueVectorType>::vv;
std::vector<MyKeyWrapper<KeyType, ValueVectorType> > &kv =
*ValueVectorSingleton<KeyType, ValueVectorType>::kv;
size_t ai = &kv.front() - &a, bi = &kv.front() - &b; // get indices in key vector
std::swap(a, b); // swap keys
std::swap(vv[ai], vv[bi]); // and any associated values
}
} // ~std
And sorting as:
std::vector<char> vv; // filled by values
std::vector<MyKeyWrapper<size_t, std::vector<char> > > kv; // filled by keys, casted to MyKeyWrapper
ValueVectorSingleton<size_t, std::vector<char> >::StartSort(kv, vv);
std::sort(kv.begin(), kv.end());
ValueVectorSingleton<size_t, std::vector<char> >::EndSort();
// trick std::sort into using the custom std::swap which also swaps the other vectors
This is obviously very appalling, trivial to abuse in horrible ways, but arguably much shorter than the pair of iterators and probably similar in performance. And it actually works.
Note that swap() could be implemented inside ValueVectorSingleton and the one injected in the std namespace would just call it. That would avoid having to make vv and kv public. Also, the addresses of a and b could further be checked to make sure they are inside kv and not some other vector. Also, this is limited to sorting by values of only one vector (can't sort by corresponding values in both vectors at the same time). And the template parameters could be simply KeyType and ValueType, this was written in a hurry.
Here is a solution I once used to sort an array together with an array of indices (--maybe it is from somewhere over here?):
template <class iterator>
class IndexComparison
{
public:
IndexComparison (iterator const& _begin, iterator const& _end) :
begin (_begin),
end (_end)
{}
bool operator()(size_t a, size_t b) const
{
return *std::next(begin,a) < *std::next(begin,b);
}
private:
const iterator begin;
const iterator end;
};
Usage:
std::vector<int> values{5,2,5,1,9};
std::vector<size_t> indices(values.size());
std::iota(indices.begin(),indices.end(),0);
std::sort(indices.begin(),indices.end()
, IndexComparison<decltype(values.cbegin())>(values.cbegin(),values.cend()));
Afterwards, the integers in vector indices are permuted such that they correspond to increasing values in the vector values. It is easy to extend this from less-comparison to general comparison functions.
Next, in order to sort also the values, you can do another
std::sort(values.begin(),values.end());
using the same comparison function. This is the solution for the lazy ones. Of course, you can alternatively also use the sorted indices according to
auto temp=values;
for(size_t i=0;i<indices.size();++i)
{
values[i]=temp[indices[i]];
}
DEMO
EDIT: I just realized that the above sorts into the opposite direction than the one you were asking for.
For supercomputing simulation purpose, I have a structure that contains two big (billions of elements) std::vector: one std::vector of "keys" (64 bits integers) and one std::vector of "values". I cannot use a std::map because in the simulations I consider, vectors are far more optimal than std::map. Moreover, I cannot use a vector of pairs because of some optimization and cache efficiency provided by separate vectors. Moreover I cannot use any extra memory.
So, considering these constaints, what is the most optimized way to sort the two vectors by increasing values of the keys ? (template metaprogramming and crazy compile-time tricks are welcome)
Two ideas off the top of my head:
Take a quicksort implementation and apply it to the "key" vector; but modify the code so that every time it does a swap on the key vector, it also performs the same swap on the value vector.
Or, perhaps more in keeping with the spirit of C++, write a custom "wrapper" iterator which iterates over both vectors at once (returning a std::pair when dereferenced). Perhaps Boost has one? You could then combine this with std::sort and a custom comparison function which considers only the "key".
EDIT:
I've used the first suggestion here for a similar problem back in a past life as a C programmer. It's far from ideal for obvious reasons, but it's possibly the quickest way to get something going.
I haven't tried a wrapper iterator like this with std::sort, but TemplateRex in the comments says it won't work, and I'm happy to defer to him on that one.
I think problem may be splitted into 2 independent parts:
How to make effective iterator for virtual map
Which sorting alorithm to use
Iterator
Implementing iterator the main problem how to return pair of key/value not creating
unnecessary copies. We can achieve it by using different types for value_type & reference. My implementation is here.
template <typename _Keys, typename _Values>
class virtual_map
{
public:
typedef typename _Keys::value_type key_type;
typedef typename _Values::value_type mapped_type;
typedef std::pair<key_type, mapped_type> value_type;
typedef std::pair<key_type&, mapped_type&> proxy;
typedef std::pair<const key_type&, const mapped_type&> const_proxy;
class iterator :
public boost::iterator_facade < iterator, value_type, boost::random_access_traversal_tag, proxy >
{
friend class boost::iterator_core_access;
public:
iterator(virtual_map *map_, size_t offset_) :
map(map_),
offset(offset_)
{}
iterator(const iterator &other_)
{
this->map = other_.map;
this->offset = other_.offset;
}
private:
bool equal(const iterator &other) const
{
assert(this->map == other.map);
return this->offset == other.offset;
}
void increment() { ++offset; }
void decrement() { --offset; }
void advance(difference_type n) { offset += n; }
reference dereference() const { return reference(map->keys[offset], map->values[offset]); }
difference_type distance_to(const iterator &other_) const { return other_.offset - this->offset; }
private:
size_t offset;
virtual_map *map;
};
public:
virtual_map(_Keys &keys_, _Values &values_) :
keys(keys_),
values(values_)
{
if(keys_.size() != values_.size())
throw std::runtime_error("different size");
}
public:
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, keys.size()); }
protected:
_Keys &keys;
_Values &values;
};
usage sample:
int main(int argc, char* const argv[])
{
std::vector<int> keys_ = { 17, 2, 13, 4, 51, 78, 49, 37, 1 };
std::vector<std::string> values_ = { "17", "2", "13", "4", "51", "78", "49", "37", "1" };
typedef virtual_map<std::vector<int>, std::vector<std::string>> map;
map map_(keys_, values_);
std::sort(std::begin(map_), std::end(map_), [](map::const_proxy left_, map::const_proxy right_)
{
return left_.first < right_.first;
});
return 0;
}
Sorting algorithm
Its very hard to reason which method better without additional details. What memory restriction do you have? Is it possible to use concurrency?
There are some issues:
Iterating both sequences together requires a pair representing
references to the sequence elements - that pair, itself, is no
reference. Hence, algorithms working on references will not work.
Performance will degenerate (the sequences are loosely coupled) -
An implementation using a pair of references and std::sort:
// Copyright (c) 2014 Dieter Lucking. Distributed under the Boost
// software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <algorithm>
#include <chrono>
#include <memory>
#include <iostream>
// None
// ============================================================================
/// A void type
struct None {
None()
{}
/// Explicit conversion to None.
template <typename T>
explicit None(const T&)
{}
template <typename T>
None& operator = (const T&) {
return *this;
}
/// Never null.
None* operator & () const;
};
extern None& none();
inline None* None::operator & () const { return &none(); }
None& none() {
static None result;
return result;
}
// IteratorAdaptorTraits
// ============================================================================
namespace Detail {
// IteratorAdaptorTraits
// =====================
template <typename Iterator, typename ReturnType, bool IsReference>
struct IteratorAdaptorTraits;
// No reference
// ============
template <typename Iterator, typename ReturnType>
struct IteratorAdaptorTraits<Iterator, ReturnType, false>
{
typedef Iterator iterator_type;
typedef ReturnType return_type;
typedef ReturnType value_type;
typedef None reference;
typedef None pointer;
static_assert(
! std::is_base_of<None, return_type>::value,
"None as return type.");
template <typename Accessor>
static return_type iterator_value(const Accessor& accessor, const Iterator& iterator) {
return accessor.value(iterator);
}
template <typename Accessor>
static pointer iterator_pointer(const Accessor& accessor, const Iterator& iterator) {
return &none();
}
};
// Reference
// =========
template <typename Iterator, typename ReturnType>
struct IteratorAdaptorTraits<Iterator, ReturnType, true>
{
typedef Iterator iterator_type;
typedef ReturnType return_type;
typedef typename std::remove_reference<ReturnType>::type value_type;
typedef ReturnType reference;
typedef value_type* pointer;
static_assert(
! std::is_base_of<None, return_type>::value,
"None as return type.");
template <typename Accessor>
static return_type iterator_value(const Accessor& accessor, const Iterator& iterator) {
return accessor.value(iterator);
}
template <typename Accessor>
static pointer iterator_pointer(const Accessor& accessor, const Iterator& iterator) {
return &accessor.value(iterator);
}
};
} // namespace Detail
// RandomAccessIteratorAdaptor
// ============================================================================
/// An adaptor around a random access iterator.
/// \ATTENTION The adaptor will not fulfill the standard iterator requierments,
/// if the accessor does not support references: In that case, the
/// reference and pointer type are None.
template <typename Iterator, typename Accessor>
class RandomAccessIteratorAdaptor
{
// Types
// =====
private:
static_assert(
! std::is_base_of<None, Accessor>::value,
"None as accessor.");
static_assert(
! std::is_base_of<None, typename Accessor::return_type>::value,
"None as return type.");
typedef typename Detail::IteratorAdaptorTraits<
Iterator,
typename Accessor::return_type,
std::is_reference<typename Accessor::return_type>::value
> Traits;
public:
typedef typename Traits::iterator_type iterator_type;
typedef Accessor accessor_type;
typedef typename std::random_access_iterator_tag iterator_category;
typedef typename std::ptrdiff_t difference_type;
typedef typename Traits::return_type return_type;
typedef typename Traits::value_type value_type;
typedef typename Traits::reference reference;
typedef typename Traits::pointer pointer;
typedef typename accessor_type::base_type accessor_base_type;
typedef RandomAccessIteratorAdaptor<iterator_type, accessor_base_type> base_type;
// Tag
// ===
public:
struct RandomAccessIteratorAdaptorTag {};
// Construction
// ============
public:
explicit RandomAccessIteratorAdaptor(
iterator_type iterator, const accessor_type& accessor = accessor_type())
: m_iterator(iterator), m_accessor(accessor)
{}
template <typename IteratorType, typename AccessorType>
explicit RandomAccessIteratorAdaptor(const RandomAccessIteratorAdaptor<
IteratorType, AccessorType>& other)
: m_iterator(other.iterator()), m_accessor(other.accessor())
{}
// Element Access
// ==============
public:
/// The underlaying accessor.
const accessor_type& accessor() const { return m_accessor; }
/// The underlaying iterator.
const iterator_type& iterator() const { return m_iterator; }
/// The underlaying iterator.
iterator_type& iterator() { return m_iterator; }
/// The underlaying iterator.
operator iterator_type () const { return m_iterator; }
/// The base adaptor.
base_type base() const {
return base_type(m_iterator, m_accessor.base());
}
// Iterator
// ========
public:
return_type operator * () const {
return Traits::iterator_value(m_accessor, m_iterator);
}
pointer operator -> () const {
return Traits::iterator_pointer(m_accessor, m_iterator);
}
RandomAccessIteratorAdaptor increment() const {
return ++RandomAccessIteratorAdaptor(*this);
}
RandomAccessIteratorAdaptor increment_n(difference_type n) const {
RandomAccessIteratorAdaptor tmp(*this);
tmp.m_iterator += n;
return tmp;
}
RandomAccessIteratorAdaptor decrement() const {
return --RandomAccessIteratorAdaptor(*this);
}
RandomAccessIteratorAdaptor decrement_n(difference_type n) const {
RandomAccessIteratorAdaptor tmp(*this);
tmp.m_iterator -= n;
return tmp;
}
RandomAccessIteratorAdaptor& operator ++ () {
++m_iterator;
return *this;
}
RandomAccessIteratorAdaptor operator ++ (int) {
RandomAccessIteratorAdaptor tmp(*this);
++m_iterator;
return tmp;
}
RandomAccessIteratorAdaptor& operator += (difference_type n) {
m_iterator += n;
return *this;
}
RandomAccessIteratorAdaptor& operator -- () {
--m_iterator;
return *this;
}
RandomAccessIteratorAdaptor operator -- (int) {
RandomAccessIteratorAdaptor tmp(*this);
--m_iterator;
return tmp;
}
RandomAccessIteratorAdaptor& operator -= (difference_type n) {
m_iterator -= n;
return *this;
}
bool equal(const RandomAccessIteratorAdaptor& other) const {
return this->m_iterator == other.m_iterator;
}
bool less(const RandomAccessIteratorAdaptor& other) const {
return this->m_iterator < other.m_iterator;
}
bool less_equal(const RandomAccessIteratorAdaptor& other) const {
return this->m_iterator <= other.m_iterator;
}
bool greater(const RandomAccessIteratorAdaptor& other) const {
return this->m_iterator > other.m_iterator;
}
bool greater_equal(const RandomAccessIteratorAdaptor& other) const {
return this->m_iterator >= other.m_iterator;
}
private:
iterator_type m_iterator;
accessor_type m_accessor;
};
template <typename Iterator, typename Accessor>
inline RandomAccessIteratorAdaptor<Iterator, Accessor> operator + (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& i,
typename RandomAccessIteratorAdaptor<Iterator, Accessor>::difference_type n) {
return i.increment_n(n);
}
template <typename Iterator, typename Accessor>
inline RandomAccessIteratorAdaptor<Iterator, Accessor> operator - (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& i,
typename RandomAccessIteratorAdaptor<Iterator, Accessor>::difference_type n) {
return i.decrement_n(n);
}
template <typename Iterator, typename Accessor>
inline typename RandomAccessIteratorAdaptor<Iterator, Accessor>::difference_type
operator - (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& a,
const RandomAccessIteratorAdaptor<Iterator, Accessor>& b) {
return a.iterator() - b.iterator();
}
template <typename Iterator, typename Accessor>
inline bool operator == (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& a,
const RandomAccessIteratorAdaptor<Iterator, Accessor>& b) {
return a.equal(b);
}
template <typename Iterator, typename Accessor>
inline bool operator != (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& a,
const RandomAccessIteratorAdaptor<Iterator, Accessor>& b) {
return ! a.equal(b);
}
template <typename Iterator, typename Accessor>
inline bool operator < (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& a,
const RandomAccessIteratorAdaptor<Iterator, Accessor>& b) {
return a.less(b);
}
template <typename Iterator, typename Accessor>
inline bool operator <= (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& a,
const RandomAccessIteratorAdaptor<Iterator, Accessor>& b) {
return a.less_equal(b);
}
template <typename Iterator, typename Accessor>
inline bool operator > (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& a,
const RandomAccessIteratorAdaptor<Iterator, Accessor>& b) {
return a.greater(b);
}
template <typename Iterator, typename Accessor>
inline bool operator >= (
const RandomAccessIteratorAdaptor<Iterator, Accessor>& a,
const RandomAccessIteratorAdaptor<Iterator, Accessor>& b) {
return a.greater_equal(b);
}
// ElementPair
// ============================================================================
/// A pair of references which can mutate to a pair of values.
/// \NOTE If the key is one or two the pair is less comparable
/// regarding the first or second element.
template <typename First, typename Second, unsigned Key = 0>
class ElementPair
{
// Types
// =====
public:
typedef First first_type;
typedef Second second_type;
// Construction
// ============
public:
/// Reference
/// \POSTCONDITION reference() returns true
ElementPair(first_type& first, second_type& second)
: m_first(&first), m_second(&second)
{}
/// Copy construction
/// \POSTCONDITION reference() returns false
ElementPair(const ElementPair& other)
: m_first(new(m_first_storage) first_type(*other.m_first)),
m_second(new(&m_second_storage) second_type(*other.m_second))
{}
/// Move construction
/// \POSTCONDITION reference() returns false
ElementPair(ElementPair&& other)
: m_first(new(m_first_storage) first_type(std::move(*other.m_first))),
m_second(new(m_second_storage) second_type(std::move(*other.m_second)))
{}
~ElementPair() {
if( ! reference()) {
reinterpret_cast<first_type*>(m_first_storage)->~first_type();
reinterpret_cast<second_type*>(m_second_storage)->~second_type();
}
}
// Assignment
// ==========
public:
/// Swap content.
void swap(ElementPair& other) {
std::swap(*m_first, *other.m_first);
std::swap(*m_second, *other.m_second);
}
/// Assign content.
ElementPair& operator = (const ElementPair& other) {
if(&other != this) {
*m_first = *other.m_first;
*m_second = *other.m_second;
}
return *this;
}
/// Assign content.
ElementPair& operator = (ElementPair&& other) {
if(&other != this) {
*m_first = std::move(*other.m_first);
*m_second = std::move(*other.m_second);
}
return *this;
}
// Element Access
// ==============
public:
/// True if the pair holds references to external elements.
bool reference() {
return (m_first != reinterpret_cast<first_type*>(m_first_storage));
}
const first_type& first() const { return *m_first; }
first_type& first() { return *m_first; }
const second_type& second() const { return *m_second; }
second_type& second() { return *m_second; }
private:
first_type* m_first;
typename std::aligned_storage<
sizeof(first_type),
std::alignment_of<first_type>::value>::type
m_first_storage[1];
second_type* m_second;
typename std::aligned_storage<
sizeof(second_type),
std::alignment_of<second_type>::value>::type
m_second_storage[1];
};
// Compare
// =======
template <typename First, typename Second>
inline bool operator < (
const ElementPair<First, Second, 1>& a,
const ElementPair<First, Second, 1>& b)
{
return (a.first() < b.first());
}
template <typename First, typename Second>
inline bool operator < (
const ElementPair<First, Second, 2>& a,
const ElementPair<First, Second, 2>& b)
{
return (a.second() < b.second());
}
// Swap
// ====
namespace std {
template <typename First, typename Second, unsigned Key>
inline void swap(
ElementPair<First, Second, Key>& a,
ElementPair<First, Second, Key>& b)
{
a.swap(b);
}
}
// SequencePairAccessor
// ============================================================================
template <typename FirstSequence, typename SecondSequence, unsigned Keys = 0>
class SequencePairAccessor
{
// Types
// =====
public:
typedef FirstSequence first_sequence_type;
typedef SecondSequence second_sequence_type;
typedef typename first_sequence_type::size_type size_type;
typedef typename first_sequence_type::value_type first_type;
typedef typename second_sequence_type::value_type second_type;
typedef typename first_sequence_type::iterator iterator;
typedef None base_type;
typedef ElementPair<first_type, second_type, Keys> return_type;
// Construction
// ============
public:
SequencePairAccessor(first_sequence_type& first, second_sequence_type& second)
: m_first_sequence(&first), m_second_sequence(&second)
{}
// Element Access
// ==============
public:
base_type base() const { return base_type(); }
return_type value(iterator pos) const {
return return_type(*pos, (*m_second_sequence)[pos - m_first_sequence->begin()]);
}
// Data
// ====
private:
first_sequence_type* m_first_sequence;
second_sequence_type* m_second_sequence;
};
This test shows a degenaration of performance (on my system) by a factor of 1.5 for const char* and a factor of 3.4 for a std::string (compared to a single vector holding std::pair(s)).
// Test
// ============================================================================
#define SAMPLE_SIZE 1e1
#define VALUE_TYPE const char*
int main() {
const unsigned samples = SAMPLE_SIZE;
typedef int key_type;
typedef VALUE_TYPE value_type;
typedef std::vector<key_type> key_sequence_type;
typedef std::vector<value_type> value_sequence_type;
typedef SequencePairAccessor<key_sequence_type, value_sequence_type, 1> accessor_type;
typedef RandomAccessIteratorAdaptor<
key_sequence_type::iterator,
accessor_type>
iterator_adaptor_type;
key_sequence_type keys;
value_sequence_type values;
keys.reserve(samples);
values.reserve(samples);
const char* words[] = { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" };
for(unsigned i = 0; i < samples; ++i) {
key_type k = i % 10;
keys.push_back(k);
values.push_back(words[k]);
}
accessor_type accessor(keys, values);
std::random_shuffle(
iterator_adaptor_type(keys.begin(), accessor),
iterator_adaptor_type(keys.end(), accessor)
);
if(samples <= 10) {
std::cout << "\nRandom:\n"
<< "======\n";
for(unsigned i = 0; i < keys.size(); ++i)
std::cout << keys[i] << ": " << values[i] << '\n';
}
typedef std::pair<key_type, value_type> pair_type;
std::vector<pair_type> ref;
for(const auto& k: keys) {
ref.push_back(pair_type(k, words[k]));
}
struct Less {
bool operator () (const pair_type& a, const pair_type& b) const {
return a.first < b.first;
}
};
auto ref_start = std::chrono::system_clock::now();
std::sort(ref.begin(), ref.end(), Less());
auto ref_end = std::chrono::system_clock::now();
auto ref_elapsed = double((ref_end - ref_start).count())
/ std::chrono::system_clock::period::den;
auto start = std::chrono::system_clock::now();
std::sort(
iterator_adaptor_type(keys.begin(), accessor),
iterator_adaptor_type(keys.end(), accessor)
);
auto end = std::chrono::system_clock::now();
auto elapsed = double((end - start).count())
/ std::chrono::system_clock::period::den;;
if(samples <= 10) {
std::cout << "\nSorted:\n"
<< "======\n";
for(unsigned i = 0; i < keys.size(); ++i)
std::cout << keys[i] << ": " << values[i] << '\n';
}
std::cout << "\nDuration sorting " << double(samples) << " samples:\n"
<< "========\n"
<< " One Vector: " << ref_elapsed << '\n'
<< "Two Vectors: " << elapsed << '\n'
<< " Factor: " << elapsed/ref_elapsed << '\n'
<< '\n';
}
(Please adjust SAMPLE_SIZE and VALUE_TYPE)
My conclusion is a sorted view into a sequence of unsorted data might be more aprropiate (but that violates the requirement of the question).
I would like to modify the following code so that I can sort using objects.
The current code is fine when sorting individual methods that return a single value.
How can I implement using methods that returns a object;
template<typename T, typename M, template<typename> class C = std::less>
struct method_comparer : std::binary_function<T, T, bool>
{
explicit method_comparer(M (T::*p)() const) : p_(p) { }
bool operator ()(T const& lhs, T const& rhs) const
{
return C<M>()((lhs.*p_)(), (rhs.*p_)());
}
private:
M (T::*p_)() const;
};
template<typename T, typename M>
method_comparer<T, M> make_method_comparer(M (T::*p)() const)
{
return method_comparer<T, M>(p);
}
template<template<typename> class C, typename T, typename M>
method_comparer<T, M, C> make_method_comparer2(M (T::*p)() const)
{
return method_comparer<T, M, C>(p);
}
Main.cpp
// works well
std::sort(vec_p2d.begin(),vec_p2d.end(),make_method_comparer(&Point2D::getX));
// Would like to implement this
std::sort(vec_l2d.begin(),vec_l2d.end(),make_method_comparer(&Line2D::getPt1));
getPt1() methods return a Point2D object which contains the values for int x and int y;
AFAICS, you can leave your code as is. The only thing you must define is a comparison operator for Point2D or whatever object you're returning:
class Point2D {
public:
friend bool operator<(const Point2D &p1, const Point2D &p2) { ...; }
...
};
You can also remove your method_comparer class and just give appropriate comparer functions to sort:
bool compare_line_points(const Line2D &l1, const Line2D &l2) {
return l1.getPt1() < l2.getPt1();
}
and
std::sort(vec_l2d.begin(), vec_l2d.end(), compare_line_points);
Depending on your requirements, these are just a bunch of one- or two-liners. No need for templates.
If you may use boost, how about:
std::sort(vec_p2d.begin(), vec_p2d.end(), boost::bind( &Point2D::getX, _1 ) < boost::bind( &Point2D::getX, _2 ) );
&
std::sort(vec_p2d.begin(), vec_p2d.end(), boost::bind( &Point2D::getPt1, _1 ) < boost::bind( &Point2D::getPt1, _2 ) );?
To narrow it down: I'm currently using Boost.Unordered. I see two possible solutions:
Define my own Equality Predicates and Hash Functions and to utilize templates (maybe is_pointer) to distinct between pointers and instances;
Simply to extend boost::hash by providing hash_value(Type* const& x) as for hashing; and add == operator overload as free function with (Type* const& x, Type* const& y) parameters as for equality checking.
I'm not sure whether both variations are actually possible, since I didn't test them. I would like to find out you handle this problem. Implementations are welcome :)
EDIT 1:
What about this?
template<class T>
struct Equals: std::binary_function<T, T, bool> {
bool operator()(T const& left, T const& right) const {
return left == right;
}
};
template<class T>
struct Equals<T*> : std::binary_function<T*, T*, bool> {
bool operator()(T* const& left, T* const& right) const {
return *left == *right;
}
};
EDIT 2:
I've just defined:
friend std::size_t hash_value(Base const& base) {
boost::hash<std::string> hash;
return hash(base.string_);
}
friend std::size_t hash_value(Base* const& base) {
return hash_value(*base);
}
And then:
Derived d1("x");
Derived d2("x");
unordered_set<Base*> set;
set.insert(&d1);
assert(set.find(&d2) == end());
Debugger says that friend std::size_t hash_value(Base* const& base) is never called (GCC 4.7). Why is that?
EDIT 3:
I found out that template <class T> std::size_t hash_value(T* const& v) in boost/functional/hash.hpp on line #215 (Boost 1.49) is Boost's specialization for pointers and it simply masks your custom implementation of hash_value such as mine in EDIT 2.
Therefore, it seems like the only way here is to create a custom Hash Functor.
For the hash function, you have a choice between specializing boost::hash (or std::hash in the newer standard) or defining a new functor class. These alternatives work equally well.
For the equality operator, you need to define a new functor, because you cannot redefine the equality operator over pointers. It's a built-in operator (defined in functional terms as bool operator==( T const *x, T const *y )) and cannot be replaced.
Both of these can be defined generically by using a templated operator() in a non-templated class.
struct indirect_equal {
template< typename X, typename Y >
bool operator() ( X const &lhs, Y const &rhs )
{ return * lhs == * rhs; }
};
Follow a similar pattern for the hasher.
Taking into consideration all edits in the original post I would like to provide complete solution which satisfies my needs:
1. Equality:
template<class T>
struct Equal: ::std::binary_function<T, T, bool> {
bool operator()(T const& left, T const& right) const {
::std::equal_to<T> equal;
return equal(left, right);
}
};
template<class T>
struct Equal<T*> : ::std::binary_function<T*, T*, bool> {
bool operator()(T* const & left, T* const & right) const {
Equal<T> equal;
return equal(*left, *right);
}
};
2. Hashing:
template<class T>
struct Hash: ::std::unary_function<T, ::std::size_t> {
::std::size_t operator()(T const & value) const {
::boost::hash<T> hash;
return hash(value);
}
};
template<class T>
struct Hash<T*> : ::std::unary_function<T*, ::std::size_t> {
::std::size_t operator()(T* const & value) const {
Hash<T> hash;
return hash(*value);
}
};
So now I can continue using Boost's hash_value and it will not get masked for pointer types by Boost's default implementation (see EDIT 3).
3. Example:
In my application I have a thin wrapper for unordered_set which now looks like that:
template<class T, class H = Hash<T>, class E = Equal<T> >
class Set {
public:
// code omitted...
bool contains(const T& element) const {
return s_.find(element) != end();
}
bool insert(const T& element) {
return s_.insert(element).second;
}
// code omitted...
private:
::boost::unordered::unordered_set<T, H, E> s_;
};
So if we have some base class:
class Base {
public:
Base(const ::std::string& string) {
if (string.empty())
throw ::std::invalid_argument("String is empty.");
string_ = string;
}
virtual ~Base() {
}
friend bool operator==(const Base& right, const Base& left) {
return typeid(right) == typeid(left) && right.string_ == left.string_;
}
friend bool operator!=(const Base& right, const Base& left) {
return !(right == left);
}
friend ::std::size_t hash_value(Base const& base) {
::boost::hash<std::string> hash;
return hash(base.string_);
}
friend ::std::size_t hash_value(Base* const& base) {
return hash_value(*base);
}
private:
::std::string string_;
};
And some derived class:
class Derived: public Base {
public:
Derived(const ::std::string& string) :
Base(string) {
}
virtual ~Derived() {
}
};
Then we can even use polymorphism (which was my primary intention BTW):
Derived d1("¯\_(ツ)_/¯");
Derived d2("¯\_(ツ)_/¯");
Set<Base*> set;
set.insert(&d1);
assert(set.contains(&d2));
Hope this helps. Any suggestions are welcome.