2016-02-02 16 views
13

Tôi có hàm băm có thể lấy bất kỳ loại đối tượng nào và băm nó, nó sử dụng std::hash nội bộ. Bởi vì std::hash không hỗ trợ loại enum tôi đã tạo ra quá tải của hàm, 1 cho enumerations sử dụng std::underlying_type và 1 cho các loại:std :: có điều kiện vs std :: enable_if

template <typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr> 
static std::size_t Hash(T const & t) 
{ 
    return std::hash<typename std::underlying_type<T>::type>()(t); 
} 

template <typename T, typename std::enable_if<!std::is_enum<T>::value>::type* = nullptr> 
static std::size_t Hash(T const & t) 
{ 
    return std::hash<T>()(t); 
} 

này đang làm việc tốt. sau đó tôi đã cố gắng để đưa tất cả vào một chức năng duy nhất với std::conditional:

template <typename T> 
static std::size_t Hash(T const & t) 
{ 
    typedef typename std::conditional<std::is_enum<T>::value, std::hash<typename std::underlying_type<T>::type>, std::hash<T>>::type Hasher; 
    return Hasher()(t); 
} 

chính:

enum test 
{ 
    TEST = 2 
}; 

int main() { 
    Hash<test>(TEST); 
    Hash<int>(5); 
    std::cin.get(); 
    return 0; 
} 

này tuy nhiên, đã cho tôi một error:

/usr/include/c++/5/type_traits:2190:38: error: 'int' is not an enumeration type typedef __underlying_type(_Tp) type;

tôi hiểu được nhưng tôi không hiểu tại sao, tôi nghĩ rằng std::conditional sẽ ngăn chặn các lỗi biên dịch thời gian này vì <int> sử dụng std::hash<T> thay vì std::hash<typename std::underlying_type<T>::type> lúc biên dịch.

Tôi đang làm gì sai ở đây, có cách nào tôi có thể hợp nhất 2 chức năng không?

+1

Trong ngắn hạn, 'điều kiện' yêu cầu cả hai nhánh phải được định dạng tốt. Việc sáp nhập cả hai nhánh có thể được thực hiện với cấu trúc đặc điểm. – Rostislav

Trả lời

11

Vấn đề là std::underlying_type không được định dạng đúng nếu đối số mẫu không phải là một sự kiện và cả hai chi nhánh std::conditional cần phải hợp lệ.

Một khả năng sẽ được thực hiện một đặc điểm safe_underlying_type mà chỉ cần trả void nếu T không phải là một enum:

template <typename T, typename = typename std::is_enum<T>::type> 
struct safe_underlying_type { 
    using type = void; 
}; 

template <typename T> 
struct safe_underlying_type<T, std::true_type> { 
    using type = std::underlying_type_t<T>; 
}; 

Hoặc bạn chỉ có thể viết một đặc điểm để có được những loại hash bạn muốn:

template <typename T, typename = typename std::is_enum<T>::type> 
struct hash_type { 
    using type = std::hash<T>; 
}; 

template <typename T> 
struct hash_type<T, std::true_type> { 
    using type = std::hash<std::underlying_type_t<T>>; 
}; 
1

Cả hai loại (nhánh) được sử dụng trong số conditional cần phải được định dạng tốt khi chúng được đánh giá tại thời gian biên dịch.

Vấn đề là khi trình biên dịch phân tích cú pháp std::conditional chính nó, tuyên bố typename std::underlying_type<T>::type cần phải được định dạng tốt (và nó không dành cho int). Nó không thực sự quan trọng kết quả là gì, nó đã không có được đến nay.

1

typename std::underlying_type<T>::type

này được tạo không đúng cho một int, nhưng nó vẫn được đánh giá tại thời gian biên dịch. Đó là lý do tại sao bạn nhận được lỗi này. Bạn đã đưa ra một giải pháp sử dụng std::enable_if, vì vậy tôi không đi vào chi tiết ở đó. Tôi nghĩ rằng nó là tốt và sử dụng std::conditional sẽ chỉ làm cho nó khó đọc hơn anyway.

5

Nếu bạn thực sự muốn sử dụng conditional, bạn sẽ cần trì hoãn đánh giá:

template<class T> struct identity { using type = T; }; 

using Hasher = std::hash<typename std::conditional<std::is_enum<T>::value, 
                std::underlying_type<T>, 
                identity<T>>::type::type>; 

Ngoài ra, std::hash nên tự nhiên hỗ trợ enums kể từ LWG 2148.

Các vấn đề liên quan