2016-04-14 19 views
0

Ich mag wie so zwei einfache abstrakte Klassen implementieren:Implementierung der abstrakten Klasse ohne Template-Argument

class Hashable { 
public: 
    virtual Int hashValue() = 0; 
}; 

template <typename T> 
class Equatable { 
    virtual Bool operator == (const T& other) = 0; 
} 

Diese Klassen mir die Möglichkeit von Teil Template-Spezialisierung in meiner neuen Wörterbuch-Klasse geben werden.

Allerdings konnte ich sie nicht zur Arbeit bringen. Hier ist die Erklärung meines Wörterbuch Klasse:

template <Hashable Key, typename Value> 
class Dictionary { 
    . 
    . 
    . 
}; 

Das Problem ist, Schlüssel auch Equatable sein sollte, weil hashability sollte es erfordern.

Also, ich habe zwei Fragen:

  1. Können wir Equatable<T> Klasse umschreiben keine Vorlage Argumente haben? Hat C++ ein Schlüsselwort, das sich auf den aktuellen Typ der Klasse bezieht?

  2. Meiner Meinung nach Hashable besser erben von Equatable Klasse. Wie erreiche ich das ohne neue Template Definition auf Hashable (wenn meine erste Frage ja beantwortet wird, ist diese dann schon gelöst)?

  3. Was wäre der beste objektorientierte Ansatz hier? Eine Interface-Klasse mit Template-Argumenten zu haben, erscheint uns nicht gut.

Vielen Dank.

+1

empfehle ich glaube, du bist eigentlich für typeclass Mechanismus suchen, die nicht wirklich gut verfügbar in C++. Es gibt einen Mechanismus von Konzepten, der seit C++ 11 geplant war, aber es hat nicht einmal C++ 17 (http://honermann.net/blog/?p=3) –

Antwort

2

Was Sie im Grunde sucht, ist Konzepte, mit denen man so etwas schreiben würde:

template <class T> 
concept bool Hashable() 
{ 
    return requires(T t, T u) { 
     {t.hashValue()} -> size_t; 
     {t == u} -> bool; 
    }; 
} 

template <Hashable Key, class Value> 
class Dictionary { 
    ... 
}; 

Aber das wird nicht einmal in C++ 17 sein.

Bis dahin können wir diese Art der Sache in C++ schreiben 14 void_t mit:

template <class...> using void_t = void; 

template <class T, class = void> 
struct Hashable : std::false_type { }; 

template <class T> 
struct Hashable<T, void_t< 
    std::enable_if_t<std::is_same<std::declval<T&>().hashValue(), std::size_t>::value>, 
    decltype(std::declval<T&>() == std::declval<T&>()) 
    >> 
: std::true_type { }; 

template <class Key, class Value> 
class Dictionary { 
    static_assert(Hashable<Key>::value, "Key must be Hashable<>"); 
    ... 
}; 

Beachten Sie, dass in beiden Fällen, wir benötigen die Key Art diese Funktionalität zu haben - wir sind nicht die Key zu erben es virtuell. Das ist viel effizienter. Kein virtueller Versand notwendig.

Was wäre der beste objektorientierte Ansatz hier?

Verwenden Sie keinen objektorientierten Ansatz.

+0

Hat der letzte Clang Konzepte oder weißt du, wann es verfügbar sein wird? – Leviathlon

+0

@Leviathlon Es ist unklar, wann (wenn * überhaupt *) Konzepte im C++ - Standard sein werden. gcc 6.0 on trunk hat die einzige Implementierung, die mir bekannt ist. – Barry

+0

FYI, ist die ganze Idee abgeleitet von https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Hashable_Protocol/index.html – Leviathlon

1

Ich glaube, dass

template <Hashable Key, typename Value> 

nicht wirklich tun, was Sie erwarten, dass es zu tun. Bedenken Sie:

template <int Key, typename Value> class x{}; 

jetzt, Sie x<1, int> und x<2, int> instanziieren können, aber diese sind nicht nur verschiedene Objekte, sondern verschiedene Arten .In diesem Fall würde Ihr Objekt Hashable Teil des Typs werden (also während der Kompilierung und nicht zur Laufzeit generiert werden).

Was Sie wahrscheinlich wollen stattdessen - wie Wojciech Frohmberg in der anderen Antwort erwähnt:

template <typename K, typename V> 
class Dict { 
... 
static_assert(std::is_base_of<K, Hashable>::value, "Only Hashable can be the key); 
} 

oder enable_if oder eine andere Vorlage Magie von type_traits enthalten.

Was Sie suchen sind Konzepte, die nicht einmal C 17 ++ gemacht hat, oder typeclasses (verfügbar in anderen Sprachen wie Haskell oder Scala)

Wenn Sie wirklich objektorientiert verwenden möchten Ansatz hier, gehen Sie für so etwas wie:

template <typename Value> 
class Dict { 
    Dict(std::shared_ptr<Hashable>, Value) 
    {} 
} 

es ist jedoch keine typische Implementierung, so würde ich es nicht

+0

Ist es möglich, ein Template-Argument mit zwei Basisklassen (dh 'zu spezialisieren Vorlage ... ')? – Leviathlon

+0

Dies sind keine Basisklassen - das sind Ihre Argumenttypen. Ihr Beispiel definiert eine Vorlage mit drei Argumenten: eines vom Typ Hashable, eines vom Typ Equatable und eines beliebigen Typs. Sie scheinen Haskells Typklassensyntax nachzuahmen - in C++ funktioniert das nicht so. Wenn Sie eine Basisklassenabhängigkeit oder das Vorhandensein einer beliebigen Methode in einem Template-Argument erzwingen wollen, müssen Sie 'type_traits' verwenden. Mit 'type_traits' können Sie beliebig viele Einschränkungen schreiben. –

+0

Natürlich wollte ich keine Vorlage mit drei Argumenten definieren, mein Beispiel wollte nur zeigen, was ich machen möchte. Trotzdem danke. – Leviathlon

Verwandte Themen