Tôi có một quá trình băm được thực hiện bằng cách sử dụng Howard Hinnant's method (băm chung dựa trên quá tải hash_append
).Loại bỏ đa hình theo cách thích hợp
Mục đích của phương pháp đó là tạo băm lớp học để "ghi nhớ" kết quả tính toán (xem kết thúc câu trả lời này), vì vậy tôi đang gặp phải một số vấn đề. Đặc biệt, xem xét các lớp có thể Input
sau đó cần phải được băm:
struct A {
virtual int do_stuff() const = 0;
virtual ~A();
};
struct B: A {
int do_stuff() const override { return 0; }
};
struct C: A {
const int u;
int do_stuff() const override { return u; }
};
struct Input {
A const& a; // store a reference to an instance of B or C
};
Bây giờ, nếu tôi muốn băm Input
, tôi sẽ có một cái gì đó như:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, Input const& input) {
hash_append(h, typeid(input));
hash_append(h, typeid(input.a));
}
Vì vậy, tôi cần một tình trạng quá tải của hash_append
cho A
:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, A const& a) {
hash_append(h, typeid(a));
}
vấn đề ở đây là tùy thuộc vào loại thời gian chạy của a
, tôi sẽ cần thêm thông tin bổ sung vào hàm băm, ví dụ: cho C
Tôi cần phải thêm u
.
Tôi nghĩ về các giải pháp sau (và nhược điểm):
thêm một phương pháp ảo để
A
mà trả về một giá trị cụ thể có thể được bổ sung vàotypeid()
băm, nhưng:- điều này có nghĩa là thêm một phương thức bên trong
A
không liên quan đến mục đích củaA
, do đó tôi không thực sự thích ý tưởng này (đặc biệt vì tôi có nhiều lớp giống nhưA
); - điều này sẽ phá vỡ khái niệm
hash_append
vì phương thức này sẽ có kiểu trả về duy nhất cho tất cả các lớp kế thừa.
- điều này có nghĩa là thêm một phương thức bên trong
làm một loạt các
dynamic_cast
bênhash_append
:- Tôi thấy điều này khá xấu xí ... đặc biệt là nếu tôi có nhiều lớp học tương tự như
A
; - điều này là dễ xảy ra lỗi: nếu ai đó thêm một trẻ em mới là
A
và không thêm dynamic_cast vào bên tronghash_append
.
- Tôi thấy điều này khá xấu xí ... đặc biệt là nếu tôi có nhiều lớp học tương tự như
Có cách nào để băm một loại đa hình, mà không cần phải sửa đổi các loại bản thân hoặc dựa trên một loạt các dynamic_cast
?
Mục tiêu cuối cùng là có thể ghi nhớ kết quả của một số chức năng nặng. Hãy phác thảo cấu trúc cơ bản của ứng dụng của tôi:
struct Input { };
struct Result { };
Result solve(Input const&);
Chức năng solve
là tính toán nặng, vì vậy tôi muốn lưu các kết quả tính toán trước đó trong tập tin sử dụng hash của Input
s, ví dụcái gì đó như:
// depends on hash_append
std::string hash(Input const&);
Result load_or_solve(Input const& input) {
auto h = hash(input);
Result result;
if (exists(h)) { // if result exists, load it
result = load(h);
}
else { // otherwize, solve + store
result = solve(input);
store(h, result);
}
return result;
}
Các load
và store
phương pháp này sẽ tải và lưu trữ kết quả từ các tập tin, mục đích là để memoize giải pháp giữa chạy khác nhau.
Nếu bạn có đề xuất về cách ghi nhớ các kết quả này mà không phải giải quyết các vấn đề trên, tôi sẽ rất vui khi đọc chúng.
Bạn có thể sử dụng điều độ đôi trong 'phiên bản hash_append' của 'A' và gửi yêu cầu đến các phiên bản thích hợp cho' b' và 'C 'khi bạn lấy lại loại, nhưng tôi không nghĩ đó chính là thứ bạn đang tìm kiếm. Bạn đã xem nó chưa? Hạn chế là bạn phải thêm boilerplate vào các lớp đó để chấp nhận khách truy cập. Nếu nó có thể làm việc cho bạn, tôi có thể đưa bình luận vào một câu trả lời chi tiết hơn. – skypjack
@skypjack Tôi xin lỗi vì tôi không hoàn toàn hiểu ý của bạn - bạn có thể viết một ví dụ nhỏ để minh họa ý nghĩa của bạn không? – Holt
Tôi có nghĩa là một cái gì đó [dọc theo dòng này] (http://coliru.stacked-crooked.com/a/c1b5c249535f7590). Đó không phải là một ví dụ làm việc nhưng bạn nên có ý tưởng. Thật không may mã ví dụ của bạn thiếu rất nhiều mã để có thể đóng gói một ví dụ cụ thể, tôi xin lỗi. Và dĩ nhiên, 'HashVisitor' có thể được _simplified_ (ít nhất, được thiết kế để bạn không phải sửa đổi nó mỗi khi bạn định nghĩa một kiểu mới) bằng cách sử dụng các mẫu variadic và kế thừa trực tiếp, nhưng cách tôi định nghĩa nó sẽ dễ dàng hơn hiểu. – skypjack