2012-05-05 33 views
5

Tôi có một lớp mẫu Baz có chứa một lớp lồng nhau Sub. Tôi muốn xác định một hàm băm cho lớp con này bởi chuyên std :: hash. Tuy nhiên, nó dường như không hoạt động.Chuyên std :: băm cho lớp lồng nhau trong một lớp mẫu

#include <functional> 

struct Foo { 
    struct Sub { 
    }; 
}; 

template <class T> 
struct Bar { 
}; 

template <class T> 
struct Baz { 
    struct Sub { 
     int x; 
    }; 
}; 

// declare hash for Foo::Sub - all right 
namespace std { 
    template <> 
    struct hash<Foo::Sub>; 
} 

// declare hash for Bar<T> - all right 
namespace std { 
    template <class T> 
    struct hash< Bar<T> >; 
} 

// declare hash function for Baz<T>::Sub - doesn't work! 
namespace std { 
    template <class T> 
    struct hash< Baz<T>::Sub >; 
} 

// Adding typename produces a different error. 
namespace std { 
    template <class T> 
    struct hash< typename Baz<T>::Sub >; 
} 

Gcc 4.5.3 phàn nàn:

$ g++ -std=c++0x -c hash.cpp 
hash.cpp:34:30: error: type/value mismatch at argument 1 in template parameter list for ‘template<class _Tp> struct std::hash’ 
hash.cpp:34:30: error: expected a type, got ‘Baz<T>::Sub’ 
hash.cpp:40:12: error: template parameters not used in partial specialization: 
hash.cpp:40:12: error:   ‘T’ 

CẬP NHẬT

Những gì tôi đang thực sự cố gắng làm là thực hiện một container mà hỗ trợ tài liệu tham khảo ổn định (không phải trong C++ ý nghĩa) cho các phần tử bên trong nó. Tôi muốn cho phép người dùng chèn các tham chiếu này vào std::unordered_set và tương tự, đồng thời sử dụng chúng để truy cập hoặc sửa đổi các thành phần hiện có một cách hiệu quả. Sau đây chỉ là một mockup, không phải là container chính xác tôi đang thực hiện. Vấn đề là xác định hàm băm cho kiểu tham chiếu.

template <class T> 
class Container { 
public: 
    class Reference { 
    public: 
     // operator==, operator!=, operator< ...., isNull() 
    private: 
     size_t index; // index into m_entries (or could be anything else) 
     // possibly more stuff 
    }; 

    Reference insert (const T &value); 
    Reference find (const T &value); 
    void remove (Reference r); 
    Reference first(); 
    Reference next (Reference prev); 

private: 
    struct Entry { T value, ... }; 

    std::vector<Entry> m_entries; 
}; 
+0

các usecase cho điều này là gì? Có lẽ sẽ dễ dàng hơn khi chỉ định hàm băm rõ ràng cho vùng chứa của bạn? –

Trả lời

2

Câu trả lời cho câu hỏi này là những gì bạn đang cố gắng làm chỉ đơn giản là không thể làm được. Trình biên dịch không thể tìm ra lớp ngoài chứa một kiểu con. Hãy xem xét lý do tại sao:

struct outer1 { typedef int Sub; }; 
struct outer2 { typedef int Sub; }; 

Làm thế nào là trình biên dịch phải tìm ra bên ngoài mà bạn muốn khi nó được một Sub? Nó không thể. Không có cách nào để làm việc này.

Trong trường hợp của bạn có thể điều khiển từ xa, mặc dù cực kỳ khó, để lấy được IFF Sub phụ thuộc vào T. Nhưng điều này sẽ yêu cầu trình biên dịch biết Sub xuất phát từ đâu và nó chỉ không.

Vì vậy, bạn chỉ đơn giản là không thể làm điều này. Không đời nào, không.

Nếu bạn cần một số cách tiếp cận chung để tìm hàm băm cho các loại của bạn thì bạn sẽ cần tạo một hàm get_hash. Nó có thể tìm kiếm một typedef "hash_type" nội bộ theo mặc định và được ghi đè cho các băm tiêu chuẩn. Rất nhiều cách gõ ...

Cách khác, đặt Sub ra khỏi lớp có chứa làm mẫu riêng và có typedef thay vì lớp bên trong. Sau đó, bạn có thể chuyên băm trên mẫu của bạn.

Nếu không, chỉ cần cung cấp băm bạn biết bạn cần tham số hàm băm cho mẫu bạn đang sử dụng.

+0

Có, bạn đã đúng; trong ví dụ này, khi trình biên dịch nhận được một 'int', nó không thể biết được hash nào được sử dụng. Tuy nhiên, trong ví dụ của tôi, tôi không sử dụng typedef, nhưng là một lớp lồng nhau, mà không phải là một bí danh cho bất kỳ điều gì khác; khi trình biên dịch nhận được một Sub, nó đã (tôi tin) biết lớp cha mẹ nó thuộc về. –

+0

@AmbrozBizjak - Nó không và nó không thể. Bạn cần phải kéo lớp bên trong của bạn ra và sử dụng một typedef thay thế. Nếu bạn không tin tôi, bạn sẽ lãng phí rất nhiều thời gian để đập đầu vào tường ... thật vậy. C++ sẽ không hỗ trợ những gì bạn đang cố gắng làm, không có cách bạn đang đi về nó. Tôi nghĩ bạn sẽ tìm thấy khoảng một nghìn câu hỏi tương tự ở đây trên stackoverflow và câu trả lời luôn giống nhau. –

+0

Được rồi, tôi hiểu, cảm ơn. Tôi đã chuyển lớp ngoài và nó hoạt động tốt ngay bây giờ. –

0

Bạn không thể chuyên mẫu trên loại phụ thuộc như vậy. Tôi sẽ thử một cái gì đó như thế này.

struct yes {}; 

template <typename T> 
yes accept_baz_sub(typename Baz<T>::Sub&&); 
void accept_baz_sub(...); 

template <class T> 
struct is_baz_sub : std::is_same<decltype(accept_baz_sub(std::declval<T>())), yes> 
{}; 

template <typename BazSub> 
using CheckedBazSub = typename std::enable_if<is_baz_sub<BazSub>::value, BazSub>::type; 

namespace std { 
    template <class BazSub> 
    struct hash<CheckedBazSub<BazSub>>; 
} 

Chỉnh sửa: Điều này không hoàn toàn hiệu quả, theo [temp.alias] §14.5.7.2 tên mẫu bí danh không bao giờ được suy luận.

Một giải pháp khác là viết đối tượng hàm băm của riêng bạn và để cho vùng chứa sử dụng (tham số mẫu thứ ba của ví dụ std::unordered_map).

+0

"lỗi: đối số mẫu mặc định có thể không được sử dụng trong chuyên môn từng phần" –

4

Chỉ cần kéo lớp Tham chiếu ra khỏi vùng chứa.

template <class Container> 
class Reference { 
public: 
    typedef typename Container::value_type value_type; // etc... 

    // operator==, operator!=, operator< ...., isNull() 
private: 
    size_t index; // index into m_entries (or could be anything else) 
    // possibly more stuff 
}; 

template <class T> 
class Container { 
public: 
    typedef ::Reference<Container> Reference; 
    friend class Reference; // If you cannot help it 

    typedef T value_type; 

    Reference insert (const T &value); 
    Reference find (const T &value); 
    void remove (Reference r); 
    Reference first(); 
    Reference next (Reference prev); 

private: 
    struct Entry { T value, ... }; 

    std::vector<Entry> m_entries; 
}; 

Chuyên như thế này:

namespace std { 
    template <typename Container> 
    struct hash<Reference<Container>>; 
} 
+0

Cảm ơn, đó là những gì tôi đã làm; tuy nhiên tôi chấp nhận câu trả lời của Eddie bởi vì anh ấy đã đề nghị nó trước. –

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