2014-01-14 5 views
14

Kann mit SFINAE überprüft werden, ob der Typ vollständig definiert ist?Mit SFINAE prüfen, ob der Typ vollständig ist oder nicht

z.

template <class T> struct hash; 
template <>  struct hash<int> {}; 

// is_defined_hash_type definition... 

enum Enum { A, B, C, D }; 

static_assert ( is_defined_hash_type<int> ::value, "hash<int> should be defined"); 
static_assert (! is_defined_hash_type<Enum>::value, "hash<Enum> should not be defined"); 

Die Lösung sollte die Hash struct nicht ändern.

Antwort

11

Sie können ein is_complete Typen Merkmal machen, unter Verwendung der Tatsache, dass es schlecht ausgebildet ist sizeof(T) für einen unvollständigen Typen T zu bewerten:

template <typename T> 
struct is_complete_helper { 
    template <typename U> 
    static auto test(U*) -> std::integral_constant<bool, sizeof(U) == sizeof(U)>; 
    static auto test(...) -> std::false_type; 
    using type = decltype(test((T*)0)); 
}; 

template <typename T> 
struct is_complete : is_complete_helper<T>::type {}; 

und es für is_defined_hash_type<T> durch die Bestimmung überprüfen verwenden, wenn hash<T> abgeschlossen ist . (Live at Coliru)

Wie Daniel in seiner Antwort sagt, ist der Nutzen einer solchen Sache begrenzt. Das Merkmal testet nicht, ob der Typ an dem Punkt im Code vollständig ist, an dem Sie abfragen. Es testet, ob der Typ an dem Punkt im Programm vollständig war, an dem das Merkmal für einen bestimmten Typ zuerst instanziiert wurde.

4

Es ist nicht möglich. Der Grund ist, dass Sie is_defined_hash_type<T> definieren müssen, aber es kann nur eine Definition geben. Aber wenn Sie später T definieren, würde die Definition von is_defined_hash_type<T> ein anderes Ergebnis ergeben, daher eine andere Definition, und das ist nicht erlaubt. Es ist eine Verletzung der ODR (Eine Definitionsregel).

+0

Was ist mit akzeptierter Antwort? – Orient

+0

@Orient Wie Casey in seiner Antwort sagte, ist diese Eigenschaft durch das Design ziemlich gebrochen. Dies ist, was ich in meiner Antwort erklärt habe - Ihr Programm wird illegal sein und selbst wenn es zu funktionieren scheint, kann es seine Bedeutung auf subtile und unvorhersehbare Weise ändern. –

+0

Ich denke, es stellt kein Problem dar, wenn die Instanziierung lokal für eine TU ist und keine externe Verknüpfung (trivial arrangiert) hat. Aber es ist sicherlich etwas zu beachten. Dies könnte auch durch Hinzufügen eines Tag-Typs gemildert werden: 'is_defined_hash_type ' – sehe

0

Das Beste, was ich mit so weit kam ist die folgende, die zumindest einen typedef mit einem gemeinsamen Namen in allen Spezialisierung der hash erfordert:

template <class T> struct hash; 
template <>  struct hash<int> { 
    typedef int value_type; 
    }; 

template<class T> 
constexpr bool is_defined_hash_type(typename hash<T>::value_type) { 
    return true; 
} 

template<class T> 
constexpr bool is_defined_hash_type(T) { 
    return false; 
} 

int main() 
{ 
    static_assert ( is_defined_hash_type<int>(0), "hash<int> should be defined"); 
    static_assert (! is_defined_hash_type< double>(0), "hash<Enum> should not be defined"); 
    return 0; 
} 

Die Syntax ist ziemlich hässlich, aufgrund der zusätzlichen Parameter (wird benötigt, um SFINAE auszulösen). Wenn Sie denken, dass es der richtige Weg ist, werde ich versuchen, es weiter zu säubern.

Haftungsausschluss: Ich bin auf keinen Fall ein C++ 11 Experte, also habe ich vielleicht einige Punkte mit neuen Funktionen verpasst. In diesem Fall Feuer nach Belieben und ich werde versuchen, die Antwort zu korrigieren.

+0

Link zu [Coliru] (http://coliru.stacked-crooked.com/a/e99682127caf1e5f) – Massimiliano