6

Có cách nào để chuyên mẫu như thế này, làm cho chuyên môn chỉ áp dụng nếu T có chức năng thành viên hash? (Lưu ý đây chỉ là một ví dụ về những gì tôi đang cố gắng làm.Tôi biết rằng nó sẽ có ý nghĩa hơn cho mỗi lớp có hàm hash để tự kiểm tra nó trong hàm thành viên operator==, nhưng tôi chỉ muốn biết loại điều này là có thể.)Có anyway để chuyên một mẫu dựa trên các thành viên của một tham số trong C + +?

template <class T> 
bool equals(const T &x, const T &y) 
{ 
    return x == y; 
} 

template <class T> // somehow check if T has a member function 'hash' 
bool equals<T>(const T &x, const T &y) 
{ 
    return x.hash() == y.hash() && x == y; 
} 

Tôi thích giải pháp tiền C++ 11 nếu có thể.

+1

Bạn có thể sử dụng các khái niệm với khái niệm trình biên dịch thử nghiệmGCC, nhưng đó sẽ không phải là tiêu chuẩn C++ trước một vài năm. – Morwenn

Trả lời

8

Đây là ví dụ từ mã của riêng tôi. Như bạn có thể đoán từ một trong những tên cấu trúc này dựa trên nguyên tắc Substitution Failure is Not an Error. Cấu trúc has_member_setOrigin xác định hai phiên bản test. Người đầu tiên không thể hài lòng nếu U không có thành viên setOrigin. Vì đó là không phải là lỗi khi thay thế mẫu, nó hoạt động như thể nó không tồn tại. Thứ tự độ phân giải cho các chức năng đa hình do đó tìm thấy test(...) mà nếu không sẽ có ưu tiên thấp hơn. Sau đó, value được xác định theo loại trả về là test.

Tiếp theo là hai định nghĩa của callSetOrigin (tương đương với equals) của bạn bằng cách sử dụng mẫu enable_if. Nếu bạn kiểm tra enable_if bạn sẽ thấy rằng nếu đối số mẫu đầu tiên là đúng thì enable_if<...>::type được xác định, nếu không thì không. Điều này một lần nữa tạo ra một lỗi thay thế trong một trong các định nghĩa của callSetOrigin sao cho chỉ có một tồn tại.

template <typename V> 
struct has_member_setOrigin 
{ 
    template <typename U, void (U::*)(const Location &)> struct SFINAE {}; 
    template <typename U> static char test(SFINAE<U, &U::setOrigin>*); 
    template <typename U> static int test(...); 
    static const bool value = sizeof(test<V>(0)) == sizeof(char); 
}; 

template<typename V> 
void callSetOrigin(typename enable_if <has_member_setOrigin<V>::value, V>::type &p, const Location &loc) const 
{ 
    p.setOrigin(loc); 
} 

template<typename V> 
void callSetOrigin(typename enable_if <!has_member_setOrigin<V>::value, V>::type &p, const Location &loc) const 
{ 
} 

Quên tôi cung cấp một định nghĩa của enable_if cũng như:

#ifndef __ENABLE_IF_ 
#define __ENABLE_IF_ 

template<bool _Cond, typename _Tp> 
struct enable_if 
{ }; 

template<typename _Tp> 
struct enable_if<true, _Tp> 
{ typedef _Tp type; }; 

#endif /* __ENABLE_IF_ */ 
+0

Đó là một địa ngục của một workaround! – Matt

+2

Đây là một câu trả lời hay, nhưng sẽ tốt hơn nếu bạn thêm một số giải thích về cách nó hoạt động. – zwol

2

Một giải pháp C++ 11. Chỉ cần sửa đổi kiểu trả về sao cho nó chỉ là các loại hợp lệ có .hash. Ngoài ra, chúng tôi sử dụng toán tử dấu phẩy để, trong khi trình biên dịch sẽ kiểm tra xem nó có thể tính toán declval<T>.hash(), nó thực sự sẽ bỏ qua điều đó và sử dụng loại true, tất nhiên là bool, loại bạn muốn.

template <class T> 
auto equals(const T &x, const T &y) -> decltype(declval<T>.hash(), true) 
{ 
    return x.hash() == y.hash() && x == y; 
} 

Tôi tin điều này được gọi là Expression SFINAE.

Thông tin chi tiết:

decltype(X,Y) cũng giống như decltype(Y) (nhờ các nhà điều hành dấu phẩy). Điều đó có nghĩa là loại trả lại của tôi ở đây về cơ bản là decltype(true), tức là bool, như mong muốn. Vậy tại sao tôi có declval<T>.hash()? Đó không phải chỉ là một sự lãng phí không gian?

Câu trả lời là thử nghiệm rằng T có phương thức hash. Nếu thử nghiệm này thất bại, thì việc tính toán kiểu trả về thất bại, và do đó hàm không được coi là quá tải hợp lệ và trình biên dịch sẽ tìm ở nơi khác.

Cuối cùng, nếu bạn chưa từng thấy declval trước đây, đó là một cách thực sự hữu ích để tạo đối tượng thuộc loại T trong ngữ cảnh không được đánh giá (chẳng hạn như decltype).Bạn có thể bị cám dỗ viết T() để tạo T và do đó hãy sử dụng T().hash() để gọi hash. Nhưng điều đó sẽ không hoạt động nếu T không có hàm tạo mặc định. declval giải quyết vấn đề này.

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