Reputation: 399
How can I create a template class that can understand if type T
is hashable or not, and if it is then use std::unodered_set
to collect elements of type T?. Otherwise, I want it to use std::set
.
This class have the same simple methods as above sets
such as insert(), find()
and so on.
template<class T>
class MySet {
public:
MySet() {}
bool find() {
//some code here
I check if type has operator()
to get information about its "hashability". For this purpose I use the following construction, which I found on this site:
template<typename T>
class HasHash {
typedef char one;
typedef long two;
template<typename C>
static one test(decltype(&C::operator()));
template<typename C>
static two test(...);
public:
enum {
value = sizeof(test<T>(0)) == sizeof(size_t)
};
};
And get is hashable or not (true or false) by typing:
HasHash<HashableType>::value
There is no way for me to use boost library.
Upvotes: 3
Views: 90
Reputation: 5409
Here's a full working example, assuming you are using std::hash
.
#include <iostream>
#include <set>
#include <unordered_set>
#include <type_traits>
#include <functional>
using std::cout;
using std::endl;
template <typename...> using void_t = void;
template <typename, typename = void>
struct is_hashable : std::false_type { };
template <typename T>
struct is_hashable<T, void_t<decltype(std::hash<T>{})>> : std::true_type { };
template <typename T>
using set = typename std::conditional<
is_hashable<T>::value, std::unordered_set<T>, std::set<T>
>::type;
template <typename T> void test() { cout << __PRETTY_FUNCTION__ << endl; }
struct dummy_type { };
int main(int argc, char* argv[]) {
cout << std::boolalpha;
cout << is_hashable<int>::value << endl;
cout << is_hashable<dummy_type>::value << endl;
test<set<int>>();
test<set<dummy_type>>();
}
The output should be similar to
true
false
void test() [with T = std::unordered_set<int, std::hash<int>, std::equal_to<int>, std::allocator<int> >]
void test() [with T = std::set<dummy_type, std::less<dummy_type>, std::allocator<dummy_type> >]
Edit:
is_hashable
specialization can also be done without the use of void_t
, like this:
template <typename T>
struct is_hashable<T, decltype(std::hash<T>{},void())> : std::true_type { };
Upvotes: 2
Reputation: 131976
If you have a is_hashable<T>
type trait (e.g. your HasHash
), then you can use std::conditional
, like so:
template <typename T>
std::conditional<is_hashable<T>::value, std::unordered_set<T>, std::set<T>>::type
or if you're using C++17, it could be simplified to:
template <typename T>
inline constexpr bool is_hashable_v = is_hashable<T>::value;
template <typename T>
std::conditional_t<is_hashable_v<T>, std::unordered_set<T>, std::set<T>>;
(assuming you've also implemented the _v
version of the trait).
For some additional discussion regarding determining hashability, this is also interesting:
here on the site.
Upvotes: 5