2012-03-10 9 views
9

Tôi đang viết hàm băm cho đối tượng của mình. Tôi đã có thể băm thùng chứa và kết hợp băm, nhờ vào Generic Hash function for all STL-containers. Nhưng lớp học của tôi cũng có enums. Tất nhiên tôi có thể tạo ra một hàm băm cho mỗi enum, nhưng nó không có vẻ giống như một ý tưởng tốt. Có thể tạo ra một số đặc tả chung cho std::hash, để nó có thể được áp dụng cho mọi enum? Tương tự, sử dụng std::enable_ifstd::is_enumC++ 11 Hàm băm cho bất kỳ loại enum nào

namespace std { 
    template <class E> 
    class hash<typename std::enable_if<std::is_enum<E>::value, E>::type> { 
    public: 
    size_t operator()(const E& e) const { 
     return std::hash<std::underlying_type<E>::type>()(e); 
    } 
    }; 
}; 

PS. Mã này không biên dịch

error: template parameters not used in partial specialization: 
error:   ‘E’ 

Trả lời

4

Có thể, bởi di chuyển thất bại chuyên môn ra khỏi danh sách đối số mẫu để việc khấu trừ tham số của E vẫn có thể xảy ra. Ví dụ, bạn có thể có nó xảy ra trên một tuyên bố sử dụng: (. Tôi thường sử dụng các loại tên sfinae vì vậy mà tôi nhớ lý do tại sao nó ở đó)

namespace std { 
    template<class E>class hash { 
    using sfinae = typename std::enable_if<std::is_enum<E>::value, E>::type; 
    public: 
    size_t operator()(const E&e) const { 
     return std::hash<typename std::underlying_type<E>::type>()(e); 
    } 
    }; 
}; 

đang

Demo:

#include <functional> 

namespace std { 
    template<class E>class hash { 
    using sfinae = typename std::enable_if<std::is_enum<E>::value, E>::type; 
    public: 
    size_t operator()(const E&e) const { 
     return std::hash<typename std::underlying_type<E>::type>()(e); 
    } 
    }; 
}; 

enum foo { BAR, BAZ }; 
class quux {}; 

int main() { 
    // Compiles. 
    std::hash<foo>foo_hash; 
    // Gives a compile-time error: no type named ‘type’ in ‘struct 
    // std::enable_if<false, quux>’. 
    std::hash<quux>quux_hash; 
    return 0; 
} 
+0

Mẫu băm này 'mẫu xác định mẫu chính 'băm', không phải là chuyên môn. Một chuyên môn sẽ trông giống như: 'mẫu băm lớp >'. Tôi thực sự không nghĩ rằng bạn có thể xác định lại mẫu chính 'std :: hash '. – cdyson37

+0

@ cdyson37 Ngoài ra, những gì bạn đề cập dường như là _partial specialization_ chuyên môn hóa đầy đủ sẽ giống như 'template <> struct hash {...};' ** Hơn nữa **, chuyên môn hóa một phần là chống lại tiêu chuẩn và được chỉ định là ** hành vi không xác định **. Xem [SO câu hỏi/câu trả lời] này (http://stackoverflow.com/questions/28077592/extending-namespace-std-via-partial-template-specialization). –

+1

Nó thực sự là một chuyên môn hóa một phần. Nhưng tôi tin rằng nó được cho phép - xem [ở đây] (http://stackoverflow.com/questions/23339298/stdhash-template-partial-specialization) – cdyson37

10

tham số E của bạn không thể suy luận, bởi vì trình biên dịch không thể biết rằng enable_if<...>::type của bạn kết thúc lên biểu thị E một lần nữa (và trong thực tế, có một số chuyên ngành của nó mà do thiết kế không làm cái đó!). Nó được gọi là "ngữ cảnh không suy luận" cho E.

Nếu hash chỉ có một thông số, không có cách nào (mà tôi biết) để SFINAE thực hiện một phần chuyên môn của bạn.

4

Nếu bạn sẵn sàng sử dụng macro, bạn có thể đổ đúng std :: hash specialization bên cạnh khai báo enum của bạn.

Nếu không, cách duy nhất tôi đã tìm thấy dễ dàng các giá trị enum băm là để khái quát kiểu băm:

struct enum_hash 
{ 
    template <typename T> 
    inline 
    typename std::enable_if<std::is_enum<T>::value, std::size_t>::type 
    operator()(T const value) const 
    { 
     return static_cast<std::size_t>(value); 
    } 
}; 

và sử dụng nó như vậy:

enum class E { a, b, c }; 
std::unordered_map<E, std:string, enum_hash> map; 
map[E::a] = "a"; 
2

Những gì bạn đang cố gắng làm là bị cấm theo tiêu chuẩn.

[namespace.std]

Hành vi của một chương trình C++ là unde fi định nghĩa nếu nó thêm tờ khai hoặc nitions de fi để namespace std hoặc một không gian tên trong namespace std trừ trường hợp quy định.

Một chương trình có thể thêm một mẫu chuyên môn đối với bất kỳ tiêu chuẩn thư viện mẫu để namespace std chỉ khi khai phụ thuộc vào một fi loại ned dùng-de và chuyên môn đáp ứng các yêu cầu thư viện tiêu chuẩn cho mẫu ban đầu và không phải là một cách rõ ràng Cấm.

Vì vậy, bạn chắc chắn có thể theo đuổi một số ý tưởng trong các câu trả lời này, nhưng bạn không thể gọi nó là std :: hash. Việc xác định mẫu 'enum_hash' của riêng bạn có vẻ như là một ý tưởng hay.