I know that it's possible to define a hash function for a struct X by defining a separate hash function struct:
struct hash_X {
size_t operator()(const X &x) const {}
bool operator()(const X &a, const X &b) const {}
};
int main() {
unordered_set<X, hash_X, hash_X> s;
}
But I'm looking for something like operator<, which can be attached to struct X itself, e.g. with set:
struct X {
bool operator<(const X &other) const {}
};
int main() {
set<X> s;
}
The end goal is something like:
struct X {
size_t operator()(void) const {}
bool operator()(const X &other) const {}
};
int main() {
unordered_set<X> s;
}
Is this possible in C++?
std::unordered_set is defined within std namespace. And it uses std::hash structures to hash many different types. If you want to be able to use std::unordered_set<X> (without adding much info to the declaration), you must create another overload of the std::hash template, so as to make it hash your structure.
You should be able to get it working by doing the following:
# include <unordered_set>
struct X {
size_t operator()(void) const {}
bool operator()(const X &other) const {}
};
namespace std {
template<>
struct hash<X> {
inline size_t operator()(const X& x) const {
// size_t value = your hash computations over x
return value;
}
};
}
int main() {
std::unordered_set<X> s;
}
Andalso, you must provide either an overload to std::equal_to, or a comparison operator (operator==()) for your structure. You should add one of the following:
struct X {
...
inline bool operator==(const X& other) const {
// bool comparison = result of comparing 'this' to 'other'
return comparison;
}
};
Or:
template <>
struct equal_to<X> {
inline bool operator()(const X& a, const X& b) const {
// bool comparison = result of comparing 'a' to 'b'
return comparison;
}
};
There is no hash operator, but you could hide the hash struct inside of your X:
struct X
{
std::string name;
struct hash
{
auto operator()( const X& x ) const
{ return std::hash< std::string >()( x.name ); }
};
};
You could even make it a friend and make name private, etc.
Live example
namespace hashing {
template<class T>
std::size_t hash(T const&t)->
std::result_of_t<std::hash<T>(T const&)>
{
return std::hash<T>{}(t);
}
struch hasher {
template<class T>
std::size_t operator()(T const&t)const{
return hash(t);
}
};
}
the above is some boilerplate that sets up an adl-based hash system.
template<class T>
using un_set=std::unordered_set<T,hashing::hasher>;
template<class K, class V>
using un_map=std::unordered_map<K,V,hashing::hasher>;
now creates two container aliases where you do not have to specify the hasher.
To add a new hashable:
struct Foo {
std::string s;
friend size_t hash(Foo const&f){
return hashing::hasher{}(s);
}
};
and Foo works in un_set and un_map.
I would add support for std containers and tuples into hashing namespace (override the function hash for them), and magically those will work too.
I'd recommend to consider a more general hash class. You could define for this class all the common hash manipulation operations that you could need:
struct ghash {
// all the values and operations you need here
};
Then in any class where you want to compute a hash, you could define a conversion operator
struct X {
operator ghash () { // conversion operator
ghash rh;
// compute the hash
return rh;
}
};
You can then easily calculate the hash:
X x;
ghash hx = x; // convert x to a hash
hx = (ghash)x; // or if you prefer to make it visible
This will make it easier to extend the use of your hash structure without reinventing the common ground for any other struct X, Y,Z that may need a hash in the future.
Live demo here
Related
What are the requirements to ensure that type equivalence can be used in the find function of a sorted container? Specifically how does mixed type comparison work.
Setup
Say I've got a struct, comparator, and set
struct Wrapper{int val;};
template<class T> struct compare {...};
template<class T> using wset=set<T,compare<T>>;
and a main function
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1}); //direct insertion
s.insert(Wrapper{10});
s.insert(11); //insertion by implicit construction
s.insert(100);
for (int x : s)
cout << x << ' ';
auto it= s.find(10);//find by equivalence
cout<<*it;
return 0;
}
I can get the code to run by adding constructors and conversion overloads to the wrapper struct, and also adding parameterized binary predicates to the compare struct. But I don't understand how its actually working.
Equivalence Implementation
From my understanding find(a) will be implemented by searching the set for an object b that satisfies !(a<b)&&!(b<a). therefore multi type equivalence should only require defining Ta<Tb and Tb<Ta ie
bool operator()(Ta a, Tb b){...}
bool operator()(Tb b, Ta a){...}
but in practice the compiler fails to make that comparison if that's all it has to go on.
Possible Options
Its seems like my only options are to
let the wrapper object be constructed by the incoming type
struct Wrapper{
Wrapper(int val):val{val}{};
int val;
};
template<class T>
struct compare {
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
};
which seems like it has an unnecessary object being constructed
to add cast operators to wrapper and the operators to compare mixed types
struct Wrapper
{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (T const & a, int const & b) const {return a.val < b;}
bool operator() (int const & a, T const & b) const {return a < b.val;}
};
which may allow unintentional implicit conversion in other places.
to add cast operators to wrapper, and an operator to compare one type at time.
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (int const & a, int const & b) const {return a < b;}
};
which like 2 may allow unintentional implicit conversion in other places.
Working examples
using a constructor, implicit conversion, and a less than operator to handle multiple types on insert and in find
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
Wrapper(int val):val{val}{};
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(11);
s.insert(100);
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
this works but it feels like its probably constructing a wrapper in order to do the find
using implicit conversion, parameterizing the comparator operators, and making the comparator transparent prevents unintentional construction
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (T const & a, int const & b) const {return a.val < b;}
bool operator() (int const & a, T const & b) const {return a < b.val;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(Wrapper{11});
s.insert(Wrapper{100});
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
but requires that for every type combination there is a converter from WT to T and a operator for both (WT,T) and (T,WT). The converter also can't be made explicit so it exposes potential accidental conversion
defining conversion between types and handle only one type per operator
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (int const & a, int const & b) const {return a < b;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(Wrapper{11});
s.insert(Wrapper{100});
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
which is simpler than option 2 but still relies on implicit conversion.
I am trying to create a vector of maps. Each map has a different comparator.
Here is what I have tried:-
#include<iostream>
#include<map>
#include<vector>
template <class T>
struct head {
virtual bool operator() (const T& x, const T& y) const = 0;
};
template <class T>
struct greater: head<T> {
bool operator() (const T& x, const T& y) const {return x>y;}
};
template <class T>
struct less: head<T> {
bool operator() (const T& x, const T& y) const {return x<y;}
};
int main()
{
std::vector<std::map<int, int, head<int>>> mp;
return 0;
}
But I am getting an error that my operator() is pure virtual in head.
Please tell me what is the correct way to achieve this?
You need a single comparator type that works for all your vectors. Like this:
template<typename T>
struct comp {
comp(bool gt) : do_greater(gt) {}
bool operator() (const T& x, const T& y) const
{
return do_greater ? x > y : x < y;
}
bool do_greater;
};
int main()
{
std::vector<std::map<int, int, comp<int>>> mp;
mp.emplace_back(false); // first map uses less-than
mp.emplace_back(true); // first map uses greater-than
}
That is, the choice of comparison function needs to be driven by the state initialized in each std::map constructor. That single ?: branch is probably better for performance than calling a virtual method every time, too.
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.
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.
If I want to create a function template, where the template parameter isn't used in the argument list, I can do it thusly:
template<T>
T myFunction()
{
//return some T
}
But the invocation must specify the 'T' to use, as the compiler doesn't know how to work it out.
myFunction<int>();
But, suppose I wanted to do something similar, but for the '[]' operator.
template
T SomeObject::operator [ unsigned int ]
{
//Return some T
}
Is there any way to invoke this operator?
This doesn't appear valid:
SomeObject a;
a<int>[3];
This should work:
class C
{
public:
template <class T>
T operator[](int n)
{
return T();
}
};
void foo()
{
C c;
int x = c.operator[]<int>(0);
}
But it's of no real value because you'd always have to specify the type, and so it looks like a very ugly function call - the point of an operator overload is to look like an operator invocation.
Boost.Program_options uses this neat syntax:
int& i = a["option"].as<int>();
Which is achieved with something like this:
class variable_value
{
public:
variable_value(const boost::any& value) : m_value(value) {}
template<class T>
const T& as() const {
return boost::any_cast<const T&>(m_value);
}
template<class T>
T& as() {
return boost::any_cast<T&>(m_value);
}
private:
boost::any m_value;
};
class variables_map
{
public:
const variable_value& operator[](const std::string& name) const
{
return m_variables[name];
}
variable_value& operator[](const std::string& name)
{
return m_variables[name];
}
private:
std::map<std::string, variable_value> m_variables;
};
You could adapt this idea to suit your own needs.
Like with any operator, the function name is operator#, so:
a.operator[]<int>(3);
You can use a.operator[]<int>(1);
But why do you want this?
This may not be an optimal solution, but you could directly call the operator as such:
a.operator[](3);
I tried this in g++ with the following test:
class MyClass {
public:
template<class T>
T operator[](unsigned int) {
// do something
return T();
}
};
int main(int argc, char* argv[]) {
MyClass test;
test.operator[]<int>(0);
//test<int>[0]; // doesn't compile, as you mentioned
return 0;
}
If you need to define operator[] then probably define the template at the class level. Something like this:
template<class T>
class C
{
public:
T operator[](int n)
{
return T();
}
};
int main()
{
C<int> c;
int x = c[0];
return 0;
}
I have a hard time coming up with an example where this would be needed (couldn't you just overload the operator instead?), but here's my thoughts anyway:
Since you cannot use the infix operator syntax with templatized operators, you might want to do the template instantiation before you call the operator. A proxy might be a way to do this.
class some_class {
private:
template<class T> class proxy {
some_class* that_;
public:
proxy(some_class* that) : that_(that) {}
T& operator[](std::size_type idx) {return that->get<T>(idx);}
};
template<class T> class const_proxy {
some_class* that_;
public:
proxy(const some_class* that) : that_(that) {}
const T& operator[](std::size_type idx) const {return that->get<T>(idx);}
};
template< typename T > proxy<T> get_array() {return proxy<T>(this);}
template< typename T > const_proxy<T> get_array() const {return proxy<T>(this);}
template< typename T > T& get(std::size_t idx) {/* whatever */}
template< typename T > const T& get(std::size_t idx) const {/* whatever */}
};
// This is a lousy use case.
// Did I already say I have a hard time imagining how to use this?
template< typename T >
void f(some_class& some_object, sid::size_t idx)
{
T& = some_object.get_array<T>()[idx];
}