2013-07-07 32 views
7

Tôi đoán std::hash được định nghĩa là cấu trúc mẫu để tránh chuyển đổi loại tiềm ẩn được thực hiện trong quá trình phân giải chức năng quá tải. Đó có phải là điều đúng để nói không?Tại sao std :: hash không phải là một hàm bị quá tải?

Ý tôi là, tôi muốn viết

std::string s; 
size_t hash = std::hash(s); 

thay vì

std::string s; 
size_t hash = std::hash<std::string>()(s); 

Nhưng tôi đoán có một lý do giải thích tại sao Ủy ban tiêu chuẩn đã chọn tùy chọn thứ hai.

Chỉnh sửa: đoạn mã thứ hai cố định.

+0

@RiaD Đúng, cảm ơn. Đột nhiên, nó cảm thấy khó xử hơn. –

Trả lời

10

Không thể một phần chức năng chuyên biệt mẫu, và như vậy cho người dùng định nghĩa các lớp templated, sẽ không có cách nào để chuyên std::hash nếu nó là một chức năng. (bạn chỉ có thể chuyên mẫu từ không gian tên std, nhưng không quá tải, vì vậy người dùng của lớp templated của bạn không thể tạo std::unordered_map<MyClass<whatever>, MyOtherClass>, họ sẽ bị buộc phải chọn std::unordered_map<MyClass<whatever>, MyOtherClass, ???>). Vì vậy, functor là giải pháp ở đây.

namespace std 
{ 
    template<typename T> 
    struct hash<MyVector<T>> 
    { 
     size_t operator()(const MyVector<T>& v) 
     { 
      //return hash from here 
     } 
    }; 
} 

Cách thay thế cho thư viện tiêu chuẩn sẽ được sử dụng một số SFINAE mẫu lừa để lựa chọn thành viên .hash() như là mặc định, và băm tiêu chuẩn nếu các trường hợp khác, nhưng trong hầu hết các trường hợp, bạn không thể trang bị thêm giao diện (đặc biệt nếu sử dụng mã bên thứ ba)

Các thay thế khác sẽ như thế nào std::swap không (thủ đoạn với ADL):

//somewhere in std::unordered_map 
using std::hash; 
size_t h = hash(key); 

Theo kinh nghiệm của tôi, ADL là khó khăn và không phải ai cũng nhớ về trường hợp góc. Hơn nữa, lợi thế của các functors ở đây là bạn có thể sử dụng chúng như một tham số mẫu, vì vậy bạn có thể chỉ cần cắm thêm một functor cho mẫu (như std::unordered_map<A, B, specialized_hash<A>>) nếu bạn nghĩ rằng mặc định là không phù hợp với trường hợp của bạn.

Từ nhận xét:

Nhưng bạn có thể xây dựng thêm một chút về std :: hoán đổi? Nó vẫn còn có trong C++ 11 và nó không có vấn đề với các loại do người dùng định nghĩa, phải không? Tại sao giữ nhiều khái niệm khác nhau trong STL thay vì làm cho nó nhiều hơn nhất quán?

Có một sự khác biệt nhỏ giữa std::swapstd::hash:

Trong std::hash:

  • nó có khả năng đó, ví dụ std::string băm được xác định bởi tác giả của lớp sẽ không đủ cho trường hợp của bạn, nghĩa là nó quá chung chung và bạn có thể đảm bảo trong bản đồ băm của mình rằng bạn sẽ đặt chuỗi chỉ một loại, vì vậy bạn có thể cung cấp hàm băm nhanh hơn hoặc và có ít va chạm hơn.
  • có rất nhiều loại băm cho các mục đích khác nhau nên genericity là ít quan trọng ở đây
  • trong nhiều trường hợp nó có thể cho bạn để tạo một hash tốt hơn

Trong std::swap:

  • nó không chắc rằng bạn sẽ muốn có chức năng hoán đổi của riêng bạn, nhưng bạn vẫn có thể muốn sử dụng một hàm cụ thể cho lớp này, chứ không phải là một hàm gọi chung std::swap gọi các hàm tạo bản sao.
  • trong hầu hết các trường hợp, bạn không thể tạo chức năng hoán đổi, vì nó đòi hỏi kiến ​​thức về nội dung lớp (ví dụ: std::vector có thể được thực hiện dưới dạng mảng động với con trỏ ẩn dưới dạng trường riêng tư, vì vậy bạn sẽ không có thể truy cập chúng, không phải một mình hoán đổi chúng, và thậm chí thực tế được thực hiện theo cách đó không được đảm bảo)
  • có (hoặc phải) chỉ có một lần hoán đổi.
  • thực tế, có sự cố với std::swap: các thùng chứa chuẩn cung cấp chức năng swap, std::swap có thể chuyên biệt (nhưng chỉ dành cho lớp không có templated) và trao đổi có thể được định nghĩa là chức năng miễn phí được tìm thấy với ADL. Làm thế nào bạn nên cung cấp trao đổi của riêng bạn? IMO gây nhầm lẫn, không thực tế là std::swap là hàm và std::hash là một hàm.

Tại sao STL không nhất quán? Tôi chỉ có thể đoán ở đây, nhưng lý do chính tại sao STL không nhất quán là (a) khả năng tương thích bawkward và (b) C++ cũng khá không nhất quán.

+0

@milleniumbug: Bạn có một tham chiếu chuẩn cho nơi nó nói rằng bạn không thể quá tải các hàm được định nghĩa trong không gian tên 'std' không? Tôi đã có thể nghĩ rằng ADL sẽ tìm thấy hàm băm của bạn. –

+0

@ user1131467 '17.4.3.1/1 Nó không được xác định cho một chương trình C++ để thêm các khai báo hoặc các định nghĩa vào không gian tên std hoặc các không gian tên với không gian tên std trừ khi được quy định khác.Một chương trình có thể thêm chuyên môn khuôn mẫu cho bất kỳ mẫu thư viện chuẩn nào vào không gian tên std. Một chuyên ngành (đầy đủ hoặc một phần) của thư viện chuẩn dẫn đến hành vi không xác định trừ khi khai báo phụ thuộc vào tên liên kết bên ngoài do người dùng định nghĩa và trừ khi chuyên môn mẫu đáp ứng các yêu cầu thư viện chuẩn cho mẫu gốc.' (Sao chép từ http: //stackoverflow.com/a/109613/1012936) – milleniumbug

+1

Chắc chắn, nhưng nếu bạn định nghĩa 'băm (MyType)' trong không gian tên của bạn, và sau đó 'std :: unordered_map ' được gọi là 'băm (x)' từ bên trong 'std 'namespace, sau đó' std :: unordered_map 'sẽ sử dụng hàm băm của bạn do ADL (đây là một trong những điểm chính của nó). Câu hỏi là _why_ thư viện chuẩn sử dụng hàm mẫu functor chứ không phải là một hàm bị quá tải như mô tả. –

4

Một lý do khác có thể là theo cách này nó dễ dàng hơn để sử dụng nó trong các mẫu như mặc định bằng tùy chọn thay đổi

template <typrname T, typename = std::hash<T>...> 
class unordered_set; 

BTW bạn có thể tạo chức năng làm việc theo cách này

template<typename T, typename... Args> 
auto hasher(Args&&... args) -> whatever { 
    return std::hash<T>(std::forward<Args>(args)...)(//maybe some &'s skipped 
} 

hoặc (để cho phép phát hiện loại)

template<typename T, typename... Args> 
auto hasher(T t) -> whatever { 
    return std::hash<T>()(t); 
} 
+0

'struct std :: hasher {size_t toán tử() (T && x) {return std :: hash (std :: forward (x))}};' hoặc cái gì đó có hiệu lực? Tính bình đẳng được thực hiện theo cách tương tự, 'operator ==' bị quá tải và 'std :: equal_to ' làm (theo mặc định) ủy quyền cho 'operator =='. – delnan

+0

Có thể, nhưng tôi không nghĩ nó rất quan trọng vì 'băm' không phải là tính năng có thể sử dụng được (thường là các cấu trúc dữ liệu như bộ cần nó (đặc biệt là theo cách thức chung). Thêm vào đó là '==' (trong C): nó được giới thiệu trước khi 'std :: equal_to' và equal_to được thêm vào tính năng đã tồn tại – RiaD

+0

Như OP cho thấy, nó làm cho việc gọi' băm' đơn giản hơn nhiều khi bạn không quan tâm về việc cho phép một sự thay thế.Đối với indirections: Bạn đã xem các thư viện chuẩn? ;-) – delnan

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