2016-04-21 3 views
4

Предположим, у меня есть простой класс логических признаков, MyTrait. То есть для любого типа T я могу сделать MyTrait<T>::value и получить либо true, либо false. Я хотел бы специализироваться на std::hash для всех типов T, где верно MyTrait<T>::value. Есть какой-либо способ сделать это? Некоторые неудачные попытки:Специализация `std :: hash` для условий встречи классов

template <class T, typename std::enable_if< 
           MyTrait<T>::value, int 
          >::type = 0> 
struct hash<T> { 
... 
} 

Failed, потому что:

error: default template argument in a class template partial specialization 

Я также попытался положить все парциальные вещи специализации после хэша, но потом Eсти сообщения об ошибке для T быть в не выводятся контекст.

Есть ли способ сделать это? По крайней мере, один предыдущий вопрос на SO предполагает, что нет: Specializing std::hash to derived classes.

Либо решение, либо окончательное «Нет», за которым следует краткое объяснение, было бы отличным ответом.

+2

Поскольку 'станд :: hash' имеет только единственный шаблонный аргумент, единственный вид частичной специализации, который вы можете сделать, - это, например, 'template struct hash > {...}'. Я не вижу никакого способа вызвать отказ замены там, чтобы отключить специализацию. – melak47

+3

Тем не менее, вы можете предоставить собственный хэш-тип 'std :: unordered_map' и т. Д., Который вы можете оборудовать фиктивным аргументом шаблона для использования специализаций. – melak47

+0

@ melak47 Да, конечно, это вариант, но не слишком высоко для удобства использования. –

ответ

2

Учитывая шаблоны в патезрасе могут быть специализированы для любого пользователя определенного типа (1)

для того, чтобы специализироваться зЬм :: хэша для некоторого типа T, если черта истинно мы могли бы наивно написать что-то вроде (обратите внимание: не работает):

namespace std 
{ 
    template<class T> 
    struct hash<std::enable_if_t<IsCustomHashable<T>, T>> 
    { 
     ... 
    }; 
} 

это не работает, конечно, потому что

23 : error: template parameters not deducible in partial specialization:

Но даже если бы это произошло, это поставило бы нас под угрозу нарушить (1) выше.

Потому что, если кто-то просто специализировал нашу мета-функцию IsCustomHashable, чтобы она вернулась для int?

Теперь мы будем специализироваться на std :: hash для не-пользовательского типа, который запрещен.

один быстрые и безболезненный способ сделать то, что вы хотите, чтобы получить ваш станд :: хэш специализации от помощника базового класса, который сдвинут на свободную функцию:

#include <functional> 
#include <utility> 
#include <unordered_set> 


// a base class that defers to a free function. 
// the free function can be found via ADL and it can be 
// enabled/disabled with enable_if. it's up to you. 

template<class T> 
    struct impl_hash 
    { 
    using argument_type = T; 
    using result_type = std::size_t; 
    result_type operator()(const argument_type& arg) const { 
     return hash_code(arg); 
    } 
    }; 


// a test class 
struct my_hashable 
{ 
    bool operator==(const my_hashable&) const { 
    return true; 
    } 
}; 

// implement the free function in the same namespace as the argument 
// type's definition 
std::size_t hash_code(const my_hashable& mh) 
{ 
    // calculate hash here 
    return 0; 
} 

// now defining a hash specialisation becomes easy 
// you could even macroify it 
namespace std 
{ 
    template<> 
    struct hash<my_hashable> 
     : impl_hash<my_hashable> 
    { 
    }; 
} 

// check it compiles 
int main() 
{ 
    std::unordered_set<my_hashable> my_set; 
    my_set.emplace(); 
    return 0; 
} 
+0

Полезная информация, спасибо. Я осознавал макроскопический подход в широком смысле; Я надеялся избежать этого по обычным причинам. –