2016-08-02 12 views
35

Warum verwenden std::stack und std::queue type template parameter anstelle des Template-Vorlagenparameters für den zugrunde liegenden Containertyp?Warum verwendet std :: stack keinen Vorlagenparameter?

das heißt, warum stack wie folgt erklärt:

template<typename T, typename Container = deque<T>> 
class stack; 

aber nicht wie diese:

template<typename T, template<typename> class Container = deque> 
class stack; 

?

+0

@CoryKramer diese Frage ist kein Duplikat der verknüpften Frage: Die verknüpfte Frage fragt, wo wir Template/Typname setzen müssen, während diese Frage darum geht, warum eine existierende Klasse ('deque') eine bestimmte Schnittstelle hat, nicht über eine syntaktische Frage. – bennofs

+1

Funktionieren die Standardeinstellungen auch für Vorlagenvorlagenparameter? – anatolyg

+1

@anatolyg Ja, sie funktionieren. –

Antwort

38

Weil normalerweise Behälter wie std::vector have more than one template argument. Indem Sie sich nicht darum kümmern, dass es eine Vorlage ist, erlauben Sie jede Art von Container zu verwenden.

Wie würden

template<class T, class Allocator = std::allocator<T>> class vector; 

Sitz auf

template<typename> class Container 

als hätten Sie es in Ihrem stack? (Hinweis: Tut es nicht!) Sie würden für jede Zahl und jede Art von Template-Argumenten (type vs. non-type) spezielle Fälle benötigen, die Sie unterstützen möchten, was albern ist, weil diese normalerweise keine Beiträge liefern mehr Informationen als eine einfache

typename Container 

Beachten Sie, dass, um an die tatsächlichen Vorlage Argumente von z a std::vector, haben Sie die typedefs std::vector::value_type und std::vector::allocator_type, so dass die Notwendigkeit, diese Typen explizit verfügbar, wo Sie den Typ tatsächlich verwenden (d. h. die Container von stack).

+1

Autsch. Ich dachte, es wäre bequemer, in der Lage zu sein, 'std :: stack ' zu schreiben, ohne T zweimal zu spezifizieren, aber dachte nicht über Beschränkungen auf Behälterschablonenparametern nach. – Hedede

+16

@Hedede Denken Sie auch an Container, die nicht einmal aus Vorlagen instanziiert werden. Mit Ihrem Design konnten Sie nicht so etwas wie 'std :: stack ' machen. – TartanLlama

+2

Es ist viel schlimmer als "spezielle Fälle jeder _number_ von Vorlagen-Variablen", weil der Template-Template-Parameter eine Mischung aus Typ- und Nicht-Typ-Template-Argumenten haben kann. I.e. nicht nur "Container ', sondern auch "Container '. Das macht es exponentiell schlechter. – MSalters

17

Kurz gesagt: Weil die Verwendung eines Template-Vorlagenparameters restriktiver ist als die Verwendung eines Typparameters, ohne irgendwelche Vorteile zu bieten.

* Mit "restriktiv" meine ich, dass Sie möglicherweise ein komplexeres Zeug brauchen, um die gleichen Ergebnisse zu erhalten, die mit einem "einfachen" Typ-Parameter.

Warum gibt es keine Vorteile?

Ihre std::stack wahrscheinlich ein Attribut wie dies:

template <typename T, typename Container> 
struct stack { 
    Container container; 
}; 

Wenn Sie Container, durch einen Parameter Vorlage Vorlage ersetzen, warum sollten Sie erhalten?

template <typename T, template <typename...> class Container> 
struct stack { 
    Container<T> container; 
}; 

Du Instanziieren Container nur einmal und nur für T (Container<T>), so gibt es keinen Vorteil für einen Template-Template-Parameter.

Warum ist es restriktiver?

Mit einem Template-Template-Parameter, müssen Sie eine Vorlage std::stack geben, die die gleiche Signatur aussetzen, zB:

template <typename T, template <typename> class Container> 
struct stack; 

stack<int, std::vector> // Error: std::vector takes two template arguments 

Vielleicht könnten Sie variadische Vorlagen verwenden:

template <typename T, template <typename....> class Container> 
struct stack { 
    Container<T> container; 
}; 

stack<int, std::vector> // Ok, will use std::vector<int, std::allocator<int>> 

Aber was wenn ich nicht den Standard std::allocator<int> verwenden möchte?

template <typename T, 
      template <typename....> class Container = std::vector, 
      typename Allocator = std::allocator<T>> 
struct stack { 
    Container<T, Allocator> container; 
}; 

stack<int, std::vector, MyAllocator> // Ok... 

Dies ist ein bisschen chaotisch geworden ... Was passiert, wenn ich möchte, dass meine eigenen Container-Vorlagen verwenden, die 3/4/N Parameter nimmt?

template <typename T, 
      template <typename... > class Container = std::vector, 
      typename ...Args> 
struct stack { 
    Container<T, Args...> container; 
}; 

stack<int, MyTemplate, MyParam1, MyParam2> // Ok... 

Aber was, wenn ich einen nicht-Vorlagenbehälter verwenden möchte?

struct foo { }; 
struct foo_container{ }; 

stack<foo, foo_container> // Error! 

template <typename... > 
using foo_container_template = foo_container; 

stack<foo, foo_container_template> // Ok... 

Mit einem Typ-Parameter gibt es keine solchen Probleme :

stack<int> 
stack<int, std::vector<int, MyAllocator<int>> 
stack<int, MyTemplate<int, MyParam1, MyParam2>> 
stack<foo, foo_container> 

Es gibt andere Fälle, die als mit Hilfe von Vorlagen zu akzeptieren eine Mischung nicht so mit Template-Template-Parameter arbeiten von type und non-type Parameter in bestimmten Aufträgen, für die Sie generische Vorlagenparameter auch mit variadischen Vorlagen erstellen können.

+0

Ich denke nicht, dass dies die richtige Antwort ist: siehe die anderen Antworten aus einem korrekten Grund. –

+0

@KonradRudolph In C++ 11 könnten Sie 'template Klasse Container' verwenden und es würde mit einem Template-Template-Parameter arbeiten. – Holt

+0

@Holt True, fair genug. Allerdings ist die Schnittstelle natürlich älter als C++ 11 und der * Grund * für das Interface-Design ist in erster Linie die technische Einschränkung von C++ vor 11. –

13

Because it doesn’t compile:

std::deque ist nicht vom Typ

template <typename T> class std::deque 

es vom Typ ist

template<class T, class Alloc> class std::deque 

Das ist natürlich ein allgemeineres Problem: Selbst wenn wir die Alloc liefern waren Vorlagenparameter zu unserer stack Klassenvorlage, würde die Klasse jetzt nur mit Containern arbeiten, die ex haben actly zwei type template arguments. Dies ist eine unzumutbare Einschränkung.

+0

@Holt hatte hier eine nette Antwort über type-parameters, die die Verwendung von Nicht-Template-Containern wie 'struct foo; Struktur foo_container; mit foo_stack = std :: stack '. Aber wenn man über moderne Standards spricht, würde 'template ' die Verwendung solcher Container erlauben? h. kann die variadische Vorlage auf eine einfache Klasse ohne Template-Parameter "reduziert" werden? – Artalus

+0

@Artalus Gute Frage. Nein, kann es nicht. Seine Antwort ist übrigens wieder da. –

18

Die Verwendung eines Vorlagen-Vorlagenparameters würde die Typen einschränken, die Sie als zugrunde liegenden Container verwenden können, wenn Sie dieselbe Vorlagensignatur verwenden. Dieses Formular erlaubt beliebige Typen, solange sie die erwartete Schnittstelle unterstützen.

Verwandte Themen