2013-02-13 1 views
2

Есть простой способ сделать следующее с C++ 11 & Форсирование:Использование подталкивание :: hash_value определить зЬй :: хэш в C++ 11

  • использовать стандартные определения из std::hash при его наличии от <functional>
  • использовать boost::hash_value для определения std::hash в тех случаях, когда отсутствует std::hash, но boost::hash_value имеется в <boost/functional/hash.hpp>.

Например:

  • std::hash<std::vector<bool>> должно исходить из стандартной библиотеки,
  • std::hash<std::vector<unsigned>> должны быть реализованы с помощью boost::hash_value.
+2

Связанный: http://stackoverflow.com/q/12753997/743214 –

ответ

1

Ответ может быть неправильным, но я попытаюсь объяснить, почему я думаю, что ответ отрицательный.

Я не думаю, что std::hash<T> и boost:hash<T> могут использоваться взаимозаменяемо, поэтому я попытался скрыть создание объекта (даже если это не идеальное решение) и вернуть их результат size_t. Метод должен быть, конечно, выбранной во время компиляции, поэтому функция отправки что приходит на ум, образец кода:

template <typename T> 
size_t createHash(const T& t, false_type) 
{ 
    return boost::hash<T>()(t); 
} 

template <typename T> 
size_t createHash(const T& t, true_type) 
{ 
    return std::hash<T>()(t); 
} 

template<typename T> 
size_t createHash(const T& t) 
{ 
    return createHash<T>(t, std::is_XXX<T>::type()); 
} 


int main() 
{ 
    vector<unsigned> v; v.push_back(1); 
    auto h1 = createHash(v); 
    cout << " hash: " << h1; 
    //hash<vector<unsigned> > h2; 
} 

Идея этого кода проста: если вы можете построить тип типа std::hash<T>, выбрать вторую реализацию , если нет - выберите первый.

Если выбрана первая реализация, код компилируется без проблем, вы можете проверить его, используя fe. std::is_array<T>::type() в функции обертки, которая, конечно же, не соответствует действительности, поэтому будет выбрана реализация boost: hash. Однако, если вы используете черту, которая вернет true_t для vector<unsigned>, например fe. std::is_class<T>::type(), тогда компилятор сообщит, что «C++ Standard не предоставляет ...», что является результатом static_assert.

Для этого нам нужно будет принудительно заставить компилятор возвращать true_t, если тип действительно конструктивен (это не приводит к статическому_записыванию) и false_t, если это не так. Однако я не думаю, что есть возможность сделать это.

5

Первая мысль, которая приходит на ум, чтобы использовать SFINAE и попробовать std::hash<>, если это возможно, и в противном случае используйте boost::hash_value(), как это:

#include <string> 
#include <functional> 
#include <type_traits> 
#include <boost/functional/hash.hpp> 

struct my_struct_0 { 
    std::string s; 
}; 

template <typename T> 
struct has_std_hash_subst { typedef void type; }; 

template <typename T, typename C = void> 
struct has_std_hash : std::false_type {}; 

template <typename T> 
struct has_std_hash< 
    T, 
    typename has_std_hash_subst<decltype(std::hash<T>()(T())) >::type 
> : std::true_type {}; 

template <typename T> 
static typename std::enable_if<has_std_hash<T>::value, size_t>::type 
make_hash(const T &v) 
{ 
    return std::hash<T>()(v); 
} 

template <typename T> 
static typename std::enable_if<(!has_std_hash<T>::value), size_t>::type 
make_hash(const T &v) 
{ 
    return boost::hash_value(v); 
} 

int main() 
{ 
    make_hash(std::string("Hello, World!")); 
    make_hash(my_struct_0({ "Hello, World!" })); 
} 

К сожалению, всегда есть по умолчанию специализация std::hash, запускающий static_assert непринятия. Это не может быть в случае с другими библиотеками, но в случае с GCC 4.7.2 (см bits/functional_hash.h:60):

/// Primary class template hash. 
    template<typename _Tp> 
    struct hash : public __hash_base<size_t, _Tp> 
    { 
     static_assert(sizeof(_Tp) < 0, 
        "std::hash is not specialized for this type"); 
     size_t operator()(const _Tp&) const noexcept; 
    }; 

Так выше SFINAE подход не работает - static_assert там есть шоу-стоппер. Поэтому вы не можете определить, когда доступно std::hash.

Теперь это на самом деле не отвечает на ваш вопрос, но может пригодиться - это возможно сделать другим путем - сначала проверьте реализацию Boost, а затем вернитесь на std::hash<>. Рассмотрит следующий пример, который использует boost::hash_value(), если она доступна (т.е. для std::string и my_struct_0) и в противном случае использует std::hash<> (т.е. для my_struct_1):

#include <string> 
#include <functional> 
#include <type_traits> 
#include <boost/functional/hash.hpp> 

struct my_struct_0 { 
    std::string s; 
}; 

struct my_struct_1 { 
    std::string s; 
}; 

namespace boost { 
size_t hash_value(const my_struct_0 &v) { 
    return boost::hash_value(v.s); 
} 
} 

namespace std { 
template <> 
struct hash<my_struct_1> { 
    size_t operator()(const my_struct_1 &v) const { 
     return std::hash<std::string>()(v.s); 
    } 
}; 

} 

template <typename T> 
struct has_boost_hash_subst { typedef void type; }; 

template <typename T, typename C = void> 
struct has_boost_hash : std::false_type {}; 

template <typename T> 
struct has_boost_hash< 
    T, 
    typename has_boost_hash_subst<decltype(boost::hash_value(T()))>::type 
> : std::true_type {}; 

template <typename T> 
static typename std::enable_if<has_boost_hash<T>::value, size_t>::type 
make_hash(const T &v) 
{ 
    size_t ret = boost::hash_value(v); 
    std::cout << "boost::hash_value(" << typeid(T).name() 
       << ") = " << ret << '\n'; 
    return ret; 
} 

template <typename T> 
static typename std::enable_if<(!has_boost_hash<T>::value), size_t>::type 
make_hash(const T &v) 
{ 
    size_t ret = std::hash<T>()(v); 
    std::cout << "std::hash(" << typeid(T).name() 
       << ") = " << ret << '\n'; 
    return ret; 
} 

int main() 
{ 
    make_hash(std::string("Hello, World!")); 
    make_hash(my_struct_0({ "Hello, World!" })); 
    make_hash(my_struct_1({ "Hello, World!" })); 
} 

Надеется, что это помогает.

ОБНОВЛЕНИЕ: Возможно, вы могли бы использовать хак описанный here, как указано @ChristianRau, и сделать первый подход SFINAE работать! Хотя это очень грязно :)