2012-05-05 22 views
5

Ho una classe modello Baz che contiene una classe nidificata Sub. Mi piacerebbe definire una funzione di hash per questa sottoclasse specializzando std :: hash. Tuttavia, non sembra funzionare.Specializzazione std :: hash per classe nidificata in una classe modello

#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 lamenta:

$ 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’ 

UPDATE

Quello che sto davvero cercando di fare è implementare un contenitore che supporta stabili riferimenti (non in C++ senso) agli elementi al suo interno. Voglio consentire all'utente di inserire questi riferimenti in std::unordered_set e simili e usarli per accedere o modificare gli elementi esistenti in modo efficiente. Quello che segue è solo un mockup, non il contenitore esatto che sto implementando. Il problema è nel definire una funzione di hash per il tipo di riferimento.

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

Qual è il caso d'uso per questo? Forse sarà più semplice specificare semplicemente una funzione hash esplicita per il tuo contenitore? –

risposta

2

La risposta a questa domanda è che quello che stai cercando di fare è semplicemente impossibile da fare. Il compilatore non può capire quale classe esterna contiene un sottotipo. Considerate il motivo:

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

Come si suppone che il compilatore capisca quale esterno si desidera quando riceve un Sub? Non può. Non c'è modo per farlo funzionare.

Nel tuo caso potrebbe essere remoto, anche se estremamente difficile, derivare IFF Sub dipendente da T. Ma questo richiederebbe al compilatore di sapere da dove viene Sub e non lo fa.

Quindi non è possibile farlo. Assolutamente no.

Se è necessario un approccio generico per trovare una funzione di hash per i tipi, è necessario creare una metafunzione get_hash. Potrebbe cercare un typedef interno "hash_type" di default ed essere sovrascritto per gli hash standard. Lotto di digitare ...

In alternativa, mettere Sub della classe contenente come modello proprio e avere un typedef lì invece di classe interna. Quindi puoi specializzare l'hash sul tuo modello.

In caso contrario, è sufficiente fornire l'hash che si sa è necessario per il parametro della funzione di hash per il modello che si sta utilizzando.

+0

Sì, hai ragione; in questo esempio, quando il compilatore ottiene un 'int', non può sapere quale hash usare. Tuttavia, nel mio esempio, non uso un typedef, ma una classe nidificata, che non è un alias per nient'altro; quando il compilatore ottiene un sottotitolo, già (credo) sa a quale classe genitrice appartiene. –

+0

@AmbrozBizjak - Non è e non può. Devi invece tirare fuori il tuo corso interiore e usare un typedef. Se non mi credi, perderai un sacco di tempo a colpire la testa contro il muro ... davvero. C++ non supporterà ciò che stai cercando di fare, non il modo in cui lo stai facendo. Penso che troverai mille domande simili qui su StackOverflow e la risposta è sempre la stessa. –

+0

Va bene, vedo, grazie. Ho spostato la classe all'esterno e ora funziona bene. –

0

Non è possibile specializzare un modello su un tipo dipendente come quello. Vorrei provare qualcosa di simile.

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>>; 
} 

Edit: questo non funziona del tutto, secondo [temp.alias] §14.5.7.2 un nome di modello di alias è mai dedotto.

Un'altra soluzione sarebbe scrivere il proprio oggetto funzione hash e lasciare che sia utilizzato dal contenitore (terzo parametro modello di std::unordered_map per esempio).

+0

"errore: gli argomenti del modello predefinito non possono essere utilizzati nelle specializzazioni parziali" –

4

Basta estrarre la classe di riferimento dal contenitore.

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; 
}; 

specializzarsi in questo modo:

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

Grazie, è quello che ho fatto; tuttavia accettai la risposta di Eddie perché lo suggerì per primo. –

Problemi correlati