Специализиране на std::hash към производни класове

Имам абстрактен базов клас Hashable, от който произлизат класове, които могат да бъдат хеширани. Сега бих искал да разширя std::hash до всички класове, които произлизат от Hashable.

Следният код трябва да прави точно това.

#include <functional>
#include <type_traits>
#include <iostream>

class Hashable {
public:
    virtual ~Hashable() {}
    virtual std::size_t Hash() const =0;
};

class Derived : public Hashable {
public:
    std::size_t Hash() const {
        return 0;
    }
};

// Specialization of std::hash to operate on Hashable or any class derived from
// Hashable.
namespace std {
template<class C>
struct hash {
  typename std::enable_if<std::is_base_of<Hashable, C>::value, std::size_t>::type
  operator()(const C& object) const {
    return object.Hash();
  }
};
}

int main(int, char**) {
    std::hash<Derived> hasher;
    Derived d;
    std::cout << hasher(d) << std::endl;

    return 0;
}

Горният код работи точно както се очаква с gcc 4.8.1, но когато се опитам да го компилирам с gcc 4.7.2, получавам следното:

$ g++ -std=c++11 -o test test_hash.cpp
test_hash.cpp:22:8: error: redefinition of ‘struct std::hash<_Tp>’
In file included from /usr/include/c++/4.7/functional:59:0,
                 from test_hash.cpp:1:
/usr/include/c++/4.7/bits/functional_hash.h:58:12: error: previous definition of ‘struct std::hash<_Tp>’
/usr/include/c++/4.7/bits/functional_hash.h: In instantiation of ‘struct  std::hash<Derived>’:
test_hash.cpp:31:24:   required from here
/usr/include/c++/4.7/bits/functional_hash.h:60:7: error: static assertion failed:  std::hash is not specialized for this type

Може ли някой да измисли начин тази специализация на std::hash да работи за всеки клас, получен от Hashable с gcc 4.7.2?


person Flecto    schedule 20.02.2014    source източник
comment

В предварителния преглед за C# 6, Microsoft въведе синтактичната захар за деклариране на параметъра inline, както се вижда в тази статия

http://odetocode.com/blogs/scott/archive/2014/09/15/c-6-0-features-part-3-declaration-expressions.aspx

Някой знае ли защо тази функция е премахната във версията за освобождаване на .NET 4.6?

  -  person R. Martinho Fernandes    schedule 20.02.2014
comment
Дори ако пренебрегнем правилата за това какво можете и какво не ви е позволено да правите в пространството от имена на std, вашият клас е предефиниране, което не е позволено в никакъв контекст и дори да е разрешено, използването му би било двусмислено, тъй като и двете дефиниции ще съвпадат . Ще трябва или да се специализирате за всеки тип, или само за Hashable и просто да използвате std::hash<Hashable> за всички негови производни типове. Ето защо не харесвам характеристики като шаблони, а не функции чрез adl.   -  person John5342    schedule 20.02.2014
comment
Благодаря ви за вашите коментари. Подозирах, че решението ми не е съвсем кашерно и всъщност бях изненадан, че изобщо работи в gcc 4.8.1. Реших да напиша отделни специализации за всеки производен клас.   -  person Flecto    schedule 20.02.2014


Отговори (1)


Изглежда, че няма правилен начин да направя това, което исках да направя. Реших просто да напиша отделни специализации за всеки производен клас, използвайки следния макрос:

// macro to conveniently define specialization for a class derived from Hashable
#define DEFINE_STD_HASH_SPECIALIZATION(hashable)                               \
namespace std {                                                                \
template<>                                                                     \
struct hash<hashable> {                                                        \
  std::size_t operator()(const hashable& object) const {                       \
    return object.Hash();                                                      \
  }                                                                            \
};                                                                             \
}

и тогава

// specialization of std::hash for Derived
DEFINE_STD_HASH_SPECIALIZATION(Derived);
person Flecto    schedule 20.02.2014