Hy there,
I'm trying to adapt an existing code to boost::variant. The idea is to use boost::variant for a heterogeneous vector. The problem is that the rest of the code use iterators to access the elements of the vector. Is there a way to use the boost::variant with iterators?
I've tried
typedef boost::variant<Foo, Bar> Variant;
std::vector<Variant> bag;
std::vector<Variant>::iterator it;
for(it= bag.begin(); it != bag.end(); ++it){
cout<<(*it)<<endl;
}
But it didn't work.
EDIT: Thank you for your help! But in my design, I need to get one element from the list and pass it around other parts of the code (and that can be nasty, as I'm using GSL). The idea of using an iterator is that I can pass the iterator to a function, and the function will operate on the return data from that specific element. I can't see how to do that using for_each. I need to do something similar to that:
for(it=list.begin(); it!=list.end();++it) {
for(it_2=list.begin(); it_2!=list.end();++it_2) {
if(it->property() != it_2->property()) {
result = operate(it,it_2);
}
}
}
Thanks!
Well of course there is. Dereferencing the iterators will naturally yield a boost::variant<...> reference or const-reference.
However it does mean that the rest of code should be variant-aware. And notably use the boost::static_visitor to execute operations on the variants.
EDIT:
Easy!
struct Printer: boost::static_visitor<>
{
template <class T>
void operator()(T const& t) const { std::cout << t << std::endl; }
};
std::for_each(bag.begin(), bag.end(), boost::apply_visitor(Printer());
Note how writing a visitor automatically yields a predicate for STL algorithms, miam!
Now, for the issue of the return value:
class WithReturn: boost::static_visitor<>
{
public:
WithReturn(int& result): mResult(result) {}
void operator()(Foo const& f) const { mResult += f.suprise(); }
void operator()(Bar const& b) const { mResult += b.another(); }
private:
int& mResult;
};
int result;
std::for_each(bag.begin(), bag.end(), boost::apply_visitor(WithReturn(result)));
EDIT 2:
It's easy, but indeed need a bit of coaching :)
First, we remark there are 2 different operations: != and operate
struct PropertyCompare: boost::static_visitor<bool>
{
template <class T, class U>
bool operator()(T const& lhs, U const& rhs)
{
return lhs.property() == rhs.property();
}
};
struct Operate: boost::static_visitor<result_type>
{
result_type operator()(Foo const& lhs, Foo const& rhs);
result_type operator()(Foo const& lhs, Bar const& rhs);
result_type operator()(Bar const& lhs, Bar const& rhs);
result_type operator()(Bar const& lhs, Foo const& rhs);
};
for(it=list.begin(); it!=list.end();++it) {
for(it_2=list.begin(); it_2!=list.end();++it_2) {
if( !boost::apply_visitor(PropertyCompare(), *it, *it_2) ) {
result = boost::apply_visitor(Operate(), *it, *it_2));
}
}
}
For each is not that good here, because of this if. It would work if you could somehow factor the if in operate though.
Also note that I pass not iterators but references.
Related
For a C++17 restricted project I would like to have a standalone implementation of C++20 std::views::join(). I am aware of the existence of range-v3 but unfortunately the person in charge is unwilling to include further 3rd party libraries.
My aim is the write the C++17 equivalent of this (Implementation) --> solved see below
std::vector<std::vector<int>> data{{1,2},{1,2,3},{1,2,3,4}};
for(const auto & element : std::views::join(data)){
std::cout << element << "\n";
}
and the more difficult part this (Godbolt)
std::vector<std::vector<std::vector<int>>> data{{{1,2},{3,4}},{{5,6,7}}};
auto nested_join = std::views::join(std::views::join(data));
for(const auto element : nested_join){
std::cout << element << "\n";
}
I want to additionally emphasis the nesting of calls (std::views::join(std::views::join(data))), as they are the current road-block. To be more specific
How can I design my join_view class to be nestable [, while preserving the functionality which std::views::join provides (i.e. optionally modifiable elements Godbolt)?](refined/edited question)
I have successfully implemented a C++17 solution for the first part (Implementation), class code is provided at the end. The join_view wrapper class works by holding a reference to the nested object (which may or may not be const) and provides begin() and end() function to allow a range based for loop. These return an internal iterator (I think it fulfills the LegacyInputIterator requirements), for which the required operators (++,*,==,!=) are implemented.
Now lets consider my unsuccessful attempts to implement the second part.
Let's just try if I have written a super code that works as well for nesting the join_view construction. Nope. For a triple nested vector std::vector<std::vector<std::vector<int>>> and a twice applied join_view instead of a int as element type I receive a std::vector<int> (Implementation). If we take a look at the types of the nested construction auto deeper_view = join_view(join_view(data_deeper)); this expands in C++ Insights to join_view<std::vector<...>> deeper_view = join_view(join_view<std::vector<...>>(data_deeper)); which is obviously a sign of an issue?
I then tried changing all calls of the std::begin() and std::end() to their $.begin() counterpart, since these are the one defined for the join_view wrapper. Unfortunately this did also not help but now the C++ Insights output is unreadable and I cant follow it anymore.
Now I am no longer sure that this could even work therefore I am asking the question from above: How can I redesign my join_view class to be nestable?
I am aware of the following stackoverflow questions regarding std::views::join [join view how, join boost problem, join string_view problem, join compilation issue], but these do not consider implementation. I have as well tried understanding and reading the implementation of ranges-v3 join wrapper, which has provided to be difficult.
Current partially working status of standalone join_view wrapper:
template<typename T>
class join_view{
private:
T & ref_range;
using outer_iterator = decltype(ref_range.begin());
using inner_iterator = decltype((*ref_range.begin()).begin());
public:
join_view(T & range) : ref_range{range} {}
class iterator{
private:
outer_iterator outer;
inner_iterator inner;
public:
iterator(outer_iterator outer_, inner_iterator inner_): outer{outer_}, inner{inner_} {}
auto& operator*(){
return *inner;
}
auto& operator++(){
++inner;
if(inner != (*outer).end()){
return *this;
}
++outer;
inner = (*outer).begin();
return *this;
}
auto operator==(const iterator & other){
return outer == other.outer;
}
auto operator!=(const iterator & other){
return outer != other.outer;
}
};
auto begin(){
return iterator(ref_range.begin(), (*ref_range.begin()).begin());
}
auto end(){
return iterator(ref_range.end(),{});
}
};
You need to implement a recursive class template to achieve your goal. Here is a working quick and dirty prototype.
#include <cassert>
#include <iostream>
#include <type_traits>
#include <vector>
template <class T>
struct is_container : public std::false_type {};
// you'll need to declare specializations for the containers you need.
template <class T, class Alloc>
struct is_container<std::vector<T, Alloc>> : public std::true_type {};
// basic definition for our view
template <typename T, typename = void>
struct join_view;
// specialization for non-container types
template <typename T>
struct join_view<T, std::enable_if_t<!is_container<T>::value>> {
using contained_type = T;
using outer_const_iterator = const T*;
using const_iterator = const T*;
join_view(const T& t) : t_(t) {}
const_iterator begin() const { return &t_; }
const_iterator end() const { return begin() + 1; }
const T& t_;
};
// specialization for containers
template <typename Container>
struct join_view<Container, std::enable_if_t<is_container<Container>::value>> {
using contained_type = typename Container::value_type;
using outer_const_iterator = typename Container::const_iterator;
using inner_container_type = join_view<contained_type>;
using inner_const_iterator = typename inner_container_type::const_iterator;
friend inner_container_type;
class const_iterator {
friend join_view;
friend inner_container_type;
public:
const_iterator() = default;
const_iterator(const const_iterator&) = default;
const_iterator(const_iterator&&) = default;
const_iterator& operator=(const const_iterator&) = default;
const_iterator& operator=(const_iterator&&) = default;
private:
const_iterator(const Container* container, const outer_const_iterator& outer,
const inner_const_iterator& inner)
: container_(container), outer_(outer), inner_(inner) {}
const_iterator(const Container* container, outer_const_iterator outer)
: container_(container), outer_(outer) {
assert(outer_ == container_->end());
}
public:
const_iterator& operator++() {
if (++inner_ != inner_container_type{*outer_}.end()) return *this;
if (++outer_ != container_->end())
inner_ = inner_container_type{*outer_}.begin();
return *this;
}
bool operator==(const const_iterator& other) const {
if (outer_ == other.outer_) {
if (outer_ == container_->end()) return true;
return inner_ == other.inner_;
}
return false;
}
bool operator!=(const const_iterator& other) const {
return !(*this == other);
}
const auto& operator*() const
{
return *inner_;
}
private:
const Container* container_ = nullptr;
outer_const_iterator outer_;
inner_const_iterator inner_;
};
join_view(const Container& container) : outer_(container) {}
const_iterator begin() const {
return {&outer_, outer_.begin(),
inner_container_type{*(outer_.begin())}.begin()};
}
const_iterator end() const { return {&outer_, outer_.end()}; }
const Container& outer_;
};
template <typename T>
auto make_join_view(const T& t)
{
return join_view<T>(t);
}
int main() {
static_assert(is_container<std::vector<int>>::value);
static_assert(!is_container<int>::value);
int test_int = 42;
for (auto x : make_join_view(test_int)) std::cout << x << std::endl;
std::cout << std::endl;
std::vector<int> v{1, 2, 3};
for (const auto& x : make_join_view(v)) std::cout << x << std::endl;
std::cout << std::endl;
std::vector<std::vector<int>> vv{{1}, {2, 3}, {4, 5, 6}};
for (const auto& x : make_join_view(vv)) std::cout << x << std::endl;
std::cout << std::endl;
std::vector<std::vector<std::vector<int>>> vvv{ {{1}, {2, 3}, {4, 5, 6}}, {{11}, {22, 33}, {44, 55, 66}} };
for (const auto& x : make_join_view(vvv)) std::cout << x << std::endl;
}
It's very rough, as it only handles very basic traversals, but should work for most container types.
I think is_container<> can be rewritten to check for the presence of Container::const_iterator. That would allow join_view to work on any container, but also on any view.
Note: make sure your implementation uses the name const_iterator, that's absolutely imperative for this scheme to work.
You can play with the code here: https://godbolt.org/z/jhe93b1rx
I am trying to get Predecessor and Successor of a vertex from a directed graph implemented with adjacency list.
here s a brief description of my class :
template <class T>
class Digraph
{
public:
Digraph();
~Digraph();
void predecessor(T u);
void successor(T u, T v);
private:
std::map<T, std::set<T>> graphe;
}
Here is what I tried :
template <class T>
const std::set<T> Digraph<T>::predecessor(T u) const
{
std::set<T> p;
int index = 0;
for (auto it = graphe.begin(); it != graphe.end(); ++it, index++)
{
for(T el : *it) //I got the error here
{
if (el == u)
p.insert(index);
}
}
return p;
}
template <class T>
const std::set<T> Digraph<T>::successor(T u) const
{
return graphe.at(u);
}
I get the error in the inner loop.
Does anyone have an idea for an implementation? or can help me by telling me what i forgot.
Searching through the entire graph to find predecessors is a possibly costly operation. You would make your life much easier if you just stored all back-edges in a separate, identical structure:
std::map<T, std::set<T>> inverted;
Then, your member functions would be quite trivial:
template <typename T>
std::set<T> const& Digraph<T>::successor(T const& v) const {
return graph.at(v);
}
template <typename T>
std::set<T> const& Digraph<T>::predecessor(T const& u) const {
return inverted.at(u);
}
Note that the return type is an std::set<T> const& instead of an std::set<T>. This means that you don't copy the entire set.
For insertion the code becomes:
graph[v].insert(u);
inverted[u].insert(v); // new line
However, if you really want to keep your expensive lookup, maybe because it happens really seldom you can do it like this:
template <class T>
std::set<T> Digraph<T>::predecessor(T const& u) const {
std::set<T> p;
for (auto const& [v, set]: graph)
for(auto const& w : set)
if (u == w) {
p.insert(v);
break;
}
return p;
}
Please note however, that this will create copies of the values stored in your graph. If you only have integers, that's no issue. But if you have class-objects in there, copying might not be what you want.
In an attempt to write a wrapper type for another type T, I encountered a rather obnoxious problem: I would like to define some binary operators (such as +) that forward any operation on wrapper to the underlying type, but I need these operators accept any of the potential combinations that involve wrapper:
wrapper() + wrapper()
wrapper() + T()
T() + wrapper()
The naive approach involves writing all the potential overloads directly.
But I don't like writing duplicated code and wanted a bit more challenge, so I chose to implement it using a very generic template and restrict the potential types with an enable_if.
My attempt is shown at the bottom of the question (sorry, this is as minimal as I can think of). The problem is that it will run into an infinite recursion error:
To evaluate test() + test(), the compile looks at all potential overloads.
The operator defined here is in fact a potential overload, so it tries to construct the return type.
The return type has an enable_if clause, which is supposed to prevent it from being a valid overload, but the compiler just ignores that and tries to compute the decltype first, which requires ...
... an instantiation of operator+(test, test).
And we're back where we started. GCC is nice enough to spit an error; Clang just segfaults.
What would be a good, clean solution for this? (Keep in mind that there are also other operators that need to follow the same pattern.)
template<class T>
struct wrapper { T t; };
// Checks if the type is instantiated from the wrapper
template<class> struct is_wrapper : false_type {};
template<class T> struct is_wrapper<wrapper<T> > : true_type {};
// Returns the underlying object
template<class T> const T& base(const T& t) { return t; }
template<class T> const T& base(const wrapper<T>& w) { return w.t; }
// Operator
template<class W, class X>
typename enable_if<
is_wrapper<W>::value || is_wrapper<X>::value,
decltype(base(declval<W>()) + base(declval<X>()))
>::type operator+(const W& i, const X& j);
// Test case
struct test {};
int main() {
test() + test();
return 0;
}
Here's rather clunky solution that I would rather not use unless I have to:
// Force the evaluation to occur as a 2-step process
template<class W, class X, class = void>
struct plus_ret;
template<class W, class X>
struct plus_ret<W, X, typename enable_if<
is_wrapper<W>::value || is_wrapper<X>::value>::type> {
typedef decltype(base(declval<W>()) + base(declval<X>())) type;
};
// Operator
template<class W, class X>
typename plus_ret<W, X>::type operator+(const W& i, const X& j);
As an addition to the comment of TemplateRex, I would suggest use a macro to implement all overloads and take the operator as an argument:
template<class T>
struct wrapper { T t; };
#define BINARY_OPERATOR(op) \
template<class T> \
T operator op (wrapper<T> const& lhs, wrapper<T> const& rhs); \
template<class T> \
T operator op (wrapper<T> const& lhs, T const& rhs); \
template<class T> \
T operator op (T const& lhs, wrapper<T> const& rhs);
BINARY_OPERATOR(+)
BINARY_OPERATOR(-)
#undef BINARY_OPERATOR
// Test case
struct test {};
test operator+(test const&, test const&);
test operator-(test const&, test const&);
int main() {
test() + test();
wrapper<test>() + test();
test() - wrapper<test>();
return 0;
}
This is something that is touched upon on the boost page for enable_if, in an unerringly similar situation (though the error they wish to avoid is different). The solution of boost was to create a lazy_enable_if class.
The problem, as it is, is that the compiler will attempt to instantiate all the types present in the function signature, and thus the decltype(...) expression too. There is also no guarantee that the condition is computed before the type.
Unfortunately I could not come up with a solution to this issue; my latest attempt can be seen here and still triggers the maximum instantiation depth issue.
The most straightforward way to write mixed-mode arithmetic is to follow Scott Meyers's Item 24 in Effective C++
template<class T>
class wrapper1
{
public:
wrapper1(T const& t): t_(t) {} // yes, no explicit here
friend wrapper1 operator+(wrapper1 const& lhs, wrapper1 const& rhs)
{
return wrapper1{ lhs.t_ + rhs.t_ };
}
std::ostream& print(std::ostream& os) const
{
return os << t_;
}
private:
T t_;
};
template<class T>
std::ostream& operator<<(std::ostream& os, wrapper1<T> const& rhs)
{
return rhs.print(os);
}
Note that you would still need to write operator+= in order to provide a consistent interface ("do as the ints do"). If you also want to avoid that boilerplate, take a look at Boost.Operators
template<class T>
class wrapper2
:
boost::addable< wrapper2<T> >
{
public:
wrapper2(T const& t): t_(t) {}
// operator+ provided by boost::addable
wrapper2& operator+=(wrapper2 const& rhs)
{
t_ += rhs.t_;
return *this;
}
std::ostream& print(std::ostream& os) const
{
return os << t_;
}
private:
T t_;
};
template<class T>
std::ostream& operator<<(std::ostream& os, wrapper2<T> const& rhs)
{
return rhs.print(os);
}
In either case, you can then write
int main()
{
wrapper1<int> v{1};
wrapper1<int> w{2};
std::cout << (v + w) << "\n";
std::cout << (1 + w) << "\n";
std::cout << (v + 2) << "\n";
wrapper2<int> x{1};
wrapper2<int> y{2};
std::cout << (x + y) << "\n";
std::cout << (1 + y) << "\n";
std::cout << (x + 2) << "\n";
}
which will print 3 in all cases. Live example. The Boost approach is very general, e.g. you can derive from boost::arithmetic to also provide operator* from your definition of operator*=.
NOTE: this code relies on implicit conversions of T to wrapper<T>. But to quote Scott Meyers:
Classes supporting implicit type conversions are generally a bad idea.
Of course, there are exceptions to this rule, and one of the most
common is when creating numerical types.
I have a better answer for your purpose: don't make it to complicated, don't use to much meta programming. Instead use simple function to unwrap and use normal expressions. You don't need to use enable_if to remove to operators from function overload set. If they are not used the will never need to compile and if the are used they give a meaningfull error.
namespace w {
template<class T>
struct wrapper { T t; };
template<class T>
T const& unwrap(T const& t) {
return t;
}
template<class T>
T const& unwrap(wrapper<T> const& w) {
return w.t;
}
template<class T1,class T2>
auto operator +(T1 const& t1, T2 const& t2) -> decltype(unwrap(t1)+unwrap(t2)) {
return unwrap(t1)+unwrap(t2);
}
template<class T1,class T2>
auto operator -(T1 const& t1, T2 const& t2) -> decltype(unwrap(t1)-unwrap(t2)) {
return unwrap(t1)-unwrap(t2);
}
template<class T1,class T2>
auto operator *(T1 const& t1, T2 const& t2) -> decltype(unwrap(t1)*unwrap(t2)) {
return unwrap(t1)*unwrap(t2);
}
}
// Test case
struct test {};
test operator+(test const&, test const&);
test operator-(test const&, test const&);
int main() {
test() + test();
w::wrapper<test>() + w::wrapper<test>();
w::wrapper<test>() + test();
test() - w::wrapper<test>();
return 0;
}
Edit:
As an intersting additional information I have to say, the orignal soultion from fzlogic compiles under msvc 11 (but not 10). Now my solution (presented here) doesn't compile on both (gives C1045). If you need to address these isues with msvc and gcc (I don't have clang here) you have to move logic to different namespaces and functions to prevent the compiler to uses ADL and take the templated operator+ into consideration.
I'm trying to write an iterator that iterates on multiple (sorted) lists.
I have one that works but I'd like to improve it.
Here's what I have for now.
#ifndef __multi_iterator__
#define __multi_iterator__
#include <list>
template <typename T>
class multi_iterator
{
private:
typedef typename std::list<T>::const_iterator iterator;
typedef std::list<iterator> iterator_list;
public:
multi_iterator();
multi_iterator(const multi_iterator<T>& other);
multi_iterator& operator = (const multi_iterator<T>& other);
virtual ~multi_iterator();
void add_list(const std::list<T>& it);
const T& operator * ();
multi_iterator<T>& operator ++ ();
multi_iterator<T> operator ++ (int unused);
bool operator == (const multi_iterator<T>& other);
bool operator != (const multi_iterator<T>& other);
protected:
iterator_list _it_list;
iterator_list _end_list;
private:
iterator& next();
};
template <typename T>
multi_iterator<T>::multi_iterator()
: _it_list()
{
}
template <typename T>
multi_iterator<T>::multi_iterator(const multi_iterator<T>& other)
: _it_list(other._it_list)
{
}
template <typename T>
multi_iterator<T>& multi_iterator<T>::operator = (const multi_iterator<T>& other)
{
_it_list = other._it_list;
}
template <typename T>
multi_iterator<T>::~multi_iterator<T>()
{
}
template <typename T>
void multi_iterator<T>::add_list(const std::list<T>& l)
{
_it_list.push_back(l.begin());
_end_list.push_back(l.end());
}
template <typename T>
const T& multi_iterator<T>::operator * ()
{
return *(next());
}
template <typename T>
multi_iterator<T>& multi_iterator<T>::operator ++ ()
{
++(next());
return *this;
}
template <typename T>
typename multi_iterator<T>::iterator& multi_iterator<T>::next()
{
typename iterator_list::iterator it = _it_list.begin();
typename iterator_list::iterator end_it = _end_list.begin();
typename iterator_list::iterator cur_it = _it_list.end();
for (; it != _it_list.end(); ++it)
{
if (*it != *end_it)
{
if ((cur_it == _it_list.end()) || (**it < **cur_it))
{
cur_it = it;
}
}
++end_it;
}
return *cur_it;
}
template <typename T>
multi_iterator<T> multi_iterator<T>::operator ++ (int unused)
{
return ++(*this);
}
template <typename T>
bool multi_iterator<T>::operator == (const multi_iterator<T>& other)
{
return _it_list == other._it_list;
}
template <typename T>
bool multi_iterator<T>::operator != (const multi_iterator<T>& other)
{
return !(*this == other);
}
#endif /* defined(__multi_iterator__) */
Here are the questions I've been pondering:
What should a C++ iterator do when it reaches the end (trying to look like stdlib). Throw an exception?
I don't think my keeping the list of iterator "ends" is elegant. Neither is my way to find if all iterators are at the end in next(). Does anyone have a cleaner solution?
Currently next() runs in linear time and is called for both * and ++ operators. I'm thinking I could save the current iterator and get the * operator to run in constant time. Also, If I sort my list each time I call ++, would ++ run in nlog(n)? I heard that this can be done in log(n) time and I can't really find a way to do that. What are your thoughts on complexity and optimization for this?
What you're trying to do is pretty well covered by zip iterators -- the Boost.Iterator library provides one. Check out their implementation.
You should also check out this discussion if you need to be able to add containers dynamically:
Zip Several Iterators in C++
What should a C++ iterator do when it reaches the end (trying to look like stdlib). Throw an exception?
It should become singular; that is, it must remain able to be compared with other iterators from the same sequence, but does not need to be dereferencable. Specifically, it must compare equal to another past-the-end iterator, and not equal to any non-singular iterator.
It certainly shouldn't throw an exception.
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.