2015-10-09 15 views
12

Tôi có một lớp trừu tượng cho các giá trị tương đương + hashable:C++ meta-lập trình: Một số mẫu mà * phải * kế thừa một lớp trừu tượng

class Key 
{ 
public: 
    virtual bool operator ==(const Key&) const = 0; 
    virtual bool operator !=(const Key&) const = 0; 
    virtual u32 hashcode() const = 0; 
}; 

và một số lớp C bê tông mà được thừa hưởng này.

class C : public Key 
{ 
private: 
    u32 a, b; 
public: 
    static const C& null; // a prototype for representing a "no value" C 
    // Some reasonable implementation; it's just a pair 
    // ... 
}; 

và tôi muốn thực hiện một lớp HashSet templated:

template<class T inherits Key, const T& proto> class HashSet 
{ 
    //... 
}; 

T là loại giá trị được lưu trữ trong các bộ. proto phải là một thể hiện của T được sử dụng như là giá trị "null" của loại T cho mục đích đưa vào thiết lập. Tôi có kinh nghiệm hợp lý với C++ nhưng không đặc biệt với TMP và, mặc dù nó có vẻ giống như một cái gì đó đáng xấu hổ đơn giản để kéo ra, tôi dường như không thể hiểu được điều gì đó giống như mã pseudo của tôi là "class T inherits Key" được thực hiện trong C++. Tôi muốn có thể tạo một tập hợp băm các phiên bản của C như:

HashSet<C, C::null> myset; 

Ai đó có thể cho tôi biết cách xử lý hợp lý và thành ngữ trong C++ này là gì? Cảm ơn bạn!

+3

['std :: enable_if'] (http://en.cppreference.com/w/cpp/types/enable_if) và [' std :: is_base_of'] (http://en.cppreference.com/ w/cpp/types/is_base_of). – Biffen

+3

Tôi sẽ không bao giờ hiểu lý do tại sao 'std :: enable_if' bị ẩn trong danh sách tham số mẫu của mẫu chính được ưu tiên cho' static_assert' với thông điệp thân thiện với người dùng –

+2

@PiotrSkotnicki Tôi cho rằng đó là sự cân bằng giữa một trình duyệt đẹp hơn thông báo lỗi và có các ràng buộc kiểu được ghi trực tiếp trong khai báo chứ không phải là ẩn trong định nghĩa. – TartanLlama

Trả lời

11

Bạn có thể sử dụng std::enable_if_tstd::is_base_of cho việc này:

template<class T, const T& proto, 
     std::enable_if_t<std::is_base_of<Key,T>::value>* = nullptr> 
class HashSet 
{ 
    //... 
}; 

Bây giờ HashSet instantiations chỉ có giá trị nếu T thừa hưởng từ Key.

std::enable_if_t là tính năng C++ 14. Bạn có thể sử dụng typename std::enable_if<...>::type nếu bạn bị kẹt với C++ 11.

Live Demo


Một lựa chọn khác là sử dụng static_assert:

template<class T, const T& proto> 
class HashSet 
{ 
    static_assert(std::is_base_of<Key, T>::value, "T must inherit from Key"); 
}; 

Đây là có thể là một chút rõ ràng hơn và mang đến cho bạn một thông báo lỗi thân thiện hơn, nhưng kiểu chế của bạn không còn được trong tuyên bố lớp học.


Với Concepts chúng tôi sẽ nhận được rõ ràng, thông báo lỗi tốt hơn và giữ những hạn chế của chúng tôi trong việc kê khai:

template <class Base, class Derived>                                                   
concept bool IsBaseOf = std::is_base_of<Base, Derived>::value; 

template<class T, const T& proto> 
requires IsBaseOf<Key,T> 
class HashSet 
{}; 
+0

Chà, điều này có vẻ thô bạo "cải thiện" trong C++ 14 ... Đó là không có bình luận về câu trả lời của bạn tất nhiên. Tôi đánh giá cao giải pháp. Cảm ơn! – Thomas

+0

Vâng, nó khá khủng khiếp. Như Piotr đã chỉ ra trong các bình luận, bạn có thể sử dụng một 'static_assert' trong định nghĩa lớp để có được một chút rõ ràng và một thông báo lỗi đẹp hơn về chi phí của các ràng buộc kiểu của bạn được ghi trong khai báo của bạn. – TartanLlama

+4

@Thomas - đó là cách C++. Một âm mưu của một tội nham hiểm để biến ngôn ngữ trong một tội ác để giữ tỷ lệ chấp nhận của nó thấp, và tiền lương - cao;) – dtech

2

ai đó có thể vui lòng cho tôi biết những gì một cách đúng đắn và thành ngữ để xử lý tình trạng này trong C++ sẽ là gì?

Điều đó sẽ đơn giản là không xử lý. Nếu người dùng chuyển sang loại có nguồn gốc từ Key thì bản mẫu sẽ hoạt động ngay cả khi bạn không thêm điều đó làm yêu cầu rõ ràng trong chú thích mã. Nếu người dùng chuyển sang đối số mẫu không hợp lệ, thì mọi thứ được mong đợi sẽ bị hỏng.

Phiên bản tiếp theo của C++ có thể hỗ trợ rõ ràng bao gồm chú thích đó, nhưng trong phiên bản hiện tại của C++, trong khi có một số thủ thuật bạn có thể sử dụng, ngoại trừ trường hợp giới hạn. .

+1

Tôi sẽ nói rằng các thủ thuật như 'std :: enable_if' đã trở thành một cách thực tế thể hiện ràng buộc loại cho cả người dùng và trình biên dịch, nhưng tôi đoán đó là điều gây tranh cãi. – TartanLlama

+0

@TartanLlama Vâng, tôi đồng ý với điều đó, khi các ràng buộc kiểu được biểu diễn thì 'std :: enable_if' là cách thành ngữ để làm điều đó, nhưng vì nó xuất hiện như một loại hack, thể hiện ràng buộc kiểu là cái gì đó đơn giản là Nói chung chưa được thực hiện, ít nhất không phải từ những gì tôi đã nhìn thấy. :) – hvd

+0

Cảm ơn hvd. Tôi thực sự thậm chí đã không nhận ra nó sẽ không phàn nàn trừ khi tôi khởi tạo một mẫu với một T không hỗ trợ các phương pháp tôi đã sử dụng. Tôi đoán nó không đủ thông minh để kiểm tra điều này, cũng không đủ thông minh để tôi làm điều này một cách tao nhã, vì vậy ... có lẽ nó sẽ phát ra. Tôi sẽ chơi xung quanh với những giải pháp này và kiểm tra lại. Nhiều đánh giá cao! – Thomas

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