Was sind einige Verwendungen von Template-Template-Parametern?

Lesezeit: 4 Minuten

Was sind einige Verwendungen von Template Template Parametern
Ferruccio

Ich habe einige Beispiele von C++ gesehen, in denen Template-Template-Parameter verwendet werden (d. h. Templates, die Templates als Parameter verwenden), um richtlinienbasiertes Klassendesign durchzuführen. Welche anderen Anwendungen hat diese Technik?

  • Ich kam aus der anderen Richtung (FP, Haskell usw.) und landete hier: stackoverflow.com/questions/2565097/higher-kinded-types-with-c

    – Erik Kaplun

    2. März 2014 um 3:49 Uhr

Was sind einige Verwendungen von Template Template Parametern
Evan Teran

Ich denke, Sie müssen die Vorlagensyntax verwenden, um einen Parameter zu übergeben, dessen Typ eine Vorlage ist, die von einer anderen Vorlage wie dieser abhängig ist:

template <template<class> class H, class S>
void f(const H<S> &value) {
}

Hier, H ist eine Vorlage, aber ich wollte, dass diese Funktion alle Spezialisierungen von behandelt H.

HINWEIS: Ich programmiere C++ seit vielen Jahren und habe das nur einmal gebraucht. Ich finde, dass es ein selten benötigtes Feature ist (natürlich praktisch, wenn man es braucht!).

Ich habe versucht, mir gute Beispiele auszudenken, und um ehrlich zu sein, ist das meistens nicht nötig, aber lassen Sie uns ein Beispiel erfinden. Stellen wir uns das vor std::vector nicht habe einen typedef value_type.

Wie würden Sie also eine Funktion schreiben, die Variablen des richtigen Typs für die Vektorelemente erstellen kann? Das würde funktionieren.

template <template<class, class> class V, class T, class A>
void f(V<T, A> &v) {
    // This can be "typename V<T, A>::value_type",
    // but we are pretending we don't have it

    T temp = v.back();
    v.pop_back();
    // Do some work on temp

    std::cout << temp << std::endl;
}

HINWEIS: std::vector hat zwei Vorlagenparameter, Typ und Zuweisung, also mussten wir beide akzeptieren. Glücklicherweise müssen wir aufgrund der Typableitung den genauen Typ nicht explizit ausschreiben.

die Sie so verwenden können:

f<std::vector, int>(v); // v is of type std::vector<int> using any allocator

oder noch besser, wir können einfach verwenden:

f(v); // everything is deduced, f can deal with a vector of any type!

AKTUALISIEREN: Selbst dieses erfundene Beispiel ist zwar illustrativ, aber aufgrund der Einführung von c++11 kein erstaunliches Beispiel mehr auto. Jetzt kann die gleiche Funktion geschrieben werden als:

template <class Cont>
void f(Cont &v) {

    auto temp = v.back();
    v.pop_back();
    // Do some work on temp

    std::cout << temp << std::endl;
}

So würde ich diese Art von Code am liebsten schreiben.

  • Wenn f eine vom Benutzer einer Bibliothek definierte Funktion ist, ist es hässlich, dass der Benutzer std::allocator als Argument übergeben muss. Ich hätte erwartet, dass die Version ohne das Argument std::allocator mit dem Standardparameter von std::vector funktioniert hätte. Gibt es Updates zu diesem wrt C++0x?

    – amit kumar

    13. Januar 2011 um 5:19 Uhr

  • Nun, Sie müssen keinen Allocator bereitstellen. Wichtig ist, dass der Template-Template-Parameter über die richtige Anzahl von Argumenten definiert wurde. Aber die Funktion sollte sich nicht darum kümmern, was ihre “Typen” oder ihre Bedeutung sind, das Folgende funktioniert gut in C++98: template<template<class, class> class C, class T, class U> void f(C<T, U> &v)

    – pfalke

    14. Januar 2013 um 2:13 Uhr

  • Ich frage mich, warum Instanziierung ist f<vector,int> und nicht f<vector<int>>.

    – bobobobo

    17. Dezember 2013 um 16:15 Uhr

  • @bobobobo Diese beiden bedeuten unterschiedliche Dinge. f<vector,int> bedeutet f<ATemplate,AType>, f<vector<int>> bedeutet f<AType>

    – Benutzer362515

    12. Juni 2014 um 21:06 Uhr


  • @phaedrus: (viel später …) gute Punkte, verbesserte das Beispiel, um den Allocator generisch und das Beispiel klarer zu machen 🙂

    – Evan Teran

    3. August 2015 um 14:24 Uhr

1647153018 391 Was sind einige Verwendungen von Template Template Parametern
pfalke

Eigentlich ist der Anwendungsfall für Template-Template-Parameter ziemlich offensichtlich. Sobald Sie erfahren, dass C++ stdlib eine klaffende Lücke hat, Stream-Ausgabeoperatoren für Standard-Containertypen nicht zu definieren, würden Sie fortfahren, etwas zu schreiben wie:

template<typename T>
static inline std::ostream& operator<<(std::ostream& out, std::list<T> const& v)
{
    out << '[';
    if (!v.empty()) {
        for (typename std::list<T>::const_iterator i = v.begin(); ;) {
            out << *i;
            if (++i == v.end())
                break;
            out << ", ";
        }
    }
    out << ']';
    return out;
}

Dann würden Sie herausfinden, dass der Code für vector genau derselbe ist, denn forward_list ist eigentlich derselbe, selbst für eine Vielzahl von Kartentypen ist er immer noch genau derselbe. Diese Template-Klassen haben außer Meta-Schnittstelle/Protokoll nichts gemeinsam, und die Verwendung von Template-Template-Parametern ermöglicht es, die Gemeinsamkeiten in allen zu erfassen. Bevor Sie jedoch mit dem Schreiben einer Vorlage fortfahren, sollten Sie eine Referenz überprüfen, um sich daran zu erinnern, dass Sequenzcontainer zwei Vorlagenargumente akzeptieren – für Werttyp und Zuweisung. Obwohl allocator standardmäßig eingestellt ist, sollten wir seine Existenz dennoch in unserem Template-Operator << berücksichtigen:

template<template <typename, typename> class Container, class V, class A>
std::ostream& operator<<(std::ostream& out, Container<V, A> const& v)
...

Voila, das funktioniert automatisch für alle gegenwärtigen und zukünftigen Sequenzcontainer, die dem Standardprotokoll entsprechen. Um dem Mix Maps hinzuzufügen, müsste man einen Blick auf die Referenz werfen, um festzustellen, dass sie 4 Template-Parameter akzeptieren, also bräuchten wir eine andere Version des Operators<< oben mit 4-Arg-Template-Template-Param. Wir würden auch sehen, dass std:pair versucht, mit 2-arg operator<< für zuvor definierte Sequenztypen gerendert zu werden, also würden wir eine Spezialisierung nur für std::pair bereitstellen.

Übrigens, mit C+11, das variadische Templates zulässt (und daher variadische Template-Template-Argumente zulassen sollte), wäre es möglich, einen einzigen Operator<< zu haben, um sie alle zu beherrschen. Zum Beispiel:

#include <iostream>
#include <vector>
#include <deque>
#include <list>

template<typename T, template<class,class...> class C, class... Args>
std::ostream& operator <<(std::ostream& os, const C<T,Args...>& objs)
{
    os << __PRETTY_FUNCTION__ << '\n';
    for (auto const& obj : objs)
        os << obj << ' ';
    return os;
}

int main()
{
    std::vector<float> vf { 1.1, 2.2, 3.3, 4.4 };
    std::cout << vf << '\n';

    std::list<char> lc { 'a', 'b', 'c', 'd' };
    std::cout << lc << '\n';

    std::deque<int> di { 1, 2, 3, 4 };
    std::cout << di << '\n';

    return 0;
}

Ausgabe

std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = float, C = vector, Args = <std::__1::allocator<float>>]
1.1 2.2 3.3 4.4 
std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = char, C = list, Args = <std::__1::allocator<char>>]
a b c d 
std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = int, C = deque, Args = <std::__1::allocator<int>>]
1 2 3 4 

  • Dies ist ein so süßes Beispiel für Template-Template-Parameter, da es einen Fall zeigt, mit dem sich jeder auseinandersetzen musste.

    – Rabenwasser

    4. Februar 2013 um 0:24 Uhr

  • Dies ist für mich die erweckendste Antwort in C++-Vorlagen. @WhozCraig Wie haben Sie die Details der Vorlagenerweiterung erhalten?

    – Arun

    14. September 2014 um 5:51 Uhr


  • @Arun gcc unterstützt ein Makro namens __PRETTY_FUNCTION__, das unter anderem Beschreibungen von Template-Parametern im Klartext ausgibt. clang tut es auch. Manchmal eine sehr praktische Funktion (wie Sie sehen können).

    – WhozCraig

    14. September 2014 um 5:53 Uhr

  • Der Vorlagenparameter hier bringt keinen wirklichen Mehrwert. Sie können genauso gut einen regulären Vorlagenparameter als eine beliebige Instanz einer Klassenvorlage verwenden.

    – David Stein

    4. Oktober 2015 um 17:15 Uhr

  • Da muss ich David Stone zustimmen. Der Parameter template template hat hier keinen Sinn. Es wäre viel einfacher und ebenso effektiv, eine einfache Vorlage zu erstellen (Vorlage ). Ich weiß, dass dieser Beitrag ziemlich alt ist, daher füge ich nur meine 2 Cent für Leute hinzu, die über diese Antwort stolpern und nach Informationen zu Vorlagen suchen.

    – Jim Vargo

    13. Juli 2016 um 21:27 Uhr

Hier ist ein einfaches Beispiel aus ‘Modern C++ Design – Generic Programming and Design Patterns Applied’ von Andrei Alexandrescu:

Er verwendet eine Klasse mit Vorlagenparametern, um das Richtlinienmuster zu implementieren:

// Library code
template <template <class> class CreationPolicy>
class WidgetManager : public CreationPolicy<Widget>
{
   ...
};

Er erklärt:
Typischerweise kennt die Hostklasse bereits das Template-Argument der Richtlinienklasse oder kann es leicht ableiten. Im obigen Beispiel verwaltet WidgetManager immer Objekte vom Typ Widget, daher ist es redundant und potenziell gefährlich, dass der Benutzer Widget erneut in der Instanziierung von CreationPolicy angeben muss. In diesem Fall kann der Bibliothekscode Vorlagenvorlagenparameter zum Angeben von Richtlinien verwenden.

Der Effekt ist, dass der Client-Code ‘WidgetManager’ eleganter verwenden kann:

typedef WidgetManager<MyCreationPolicy> MyWidgetMgr;

Anstelle des umständlicheren und fehleranfälligeren Weges, den eine Definition ohne Template-Argumente benötigt hätte:

typedef WidgetManager< MyCreationPolicy<Widget> > MyWidgetMgr;

  • Die Frage wurde speziell für andere Beispiele als das Richtlinienmuster angefordert.

    – Benutzer2913094

    12. Februar 2015 um 11:59 Uhr

  • Genau aus diesem Buch bin ich auf diese Frage gekommen. Ein erwähnenswerter Hinweis ist, dass die Template-Template-Parameter auch im Typelist-Kapitel und in der Klassengenerierung mit Typlisten Kapitel.

    – Viktor

    29. Januar 2020 um 16:30 Uhr

1647153019 751 Was sind einige Verwendungen von Template Template Parametern
Michail Sirotenko

Hier noch ein Praxisbeispiel aus meiner CUDA Convolutional Neural Network Library. Ich habe die folgende Klassenvorlage:

template <class T> class Tensor

was tatsächlich die Manipulation von n-dimensionalen Matrizen implementiert. Es gibt auch eine untergeordnete Klassenvorlage:

template <class T> class TensorGPU : public Tensor<T>

die die gleiche Funktionalität implementiert, aber in der GPU. Beide Templates können mit allen Grundtypen wie Float, Double, Int usw. arbeiten. Und ich habe auch ein Klassen-Template (vereinfacht):

template <template <class> class TT, class T> class CLayerT: public Layer<TT<T> >
{
    TT<T> weights;
    TT<T> inputs;
    TT<int> connection_matrix;
}

Der Grund dafür, hier eine Template-Template-Syntax zu haben, ist, dass ich die Implementierung der Klasse deklarieren kann

class CLayerCuda: public CLayerT<TensorGPU, float>

die sowohl Gewichte als auch Eingaben vom Typ float und auf GPU haben wird, aber connection_matrix wird immer int sein, entweder auf CPU (durch Angabe von TT = Tensor) oder auf GPU (durch Angabe von TT = TensorGPU).

Was sind einige Verwendungen von Template Template Parametern
Markus McKenna

Angenommen, Sie verwenden CRTP, um eine “Schnittstelle” für eine Reihe von untergeordneten Vorlagen bereitzustellen. und sowohl das Elternteil als auch das Kind sind in anderen Vorlagenargumenten parametrisch:

template <typename DERIVED, typename VALUE> class interface {
    void do_something(VALUE v) {
        static_cast<DERIVED*>(this)->do_something(v);
    }
};

template <typename VALUE> class derived : public interface<derived, VALUE> {
    void do_something(VALUE v) { ... }
};

typedef interface<derived<int>, int> derived_t;

Beachten Sie die Duplizierung von „int“, bei dem es sich tatsächlich um denselben Typparameter handelt, der für beide Vorlagen angegeben ist. Sie können eine Vorlagenvorlage für DERIVED verwenden, um diese Duplizierung zu vermeiden:

template <template <typename> class DERIVED, typename VALUE> class interface {
    void do_something(VALUE v) {
        static_cast<DERIVED<VALUE>*>(this)->do_something(v);
    }
};

template <typename VALUE> class derived : public interface<derived, VALUE> {
    void do_something(VALUE v) { ... }
};

typedef interface<derived, int> derived_t;

Beachten Sie, dass Sie die direkte Bereitstellung der anderen Vorlagenparameter für die eliminieren abgeleitet Vorlage; die “Schnittstelle” empfängt sie weiterhin.

Auf diese Weise können Sie auch Typedefs in der “Schnittstelle” aufbauen, die von den Typparametern abhängen, auf die von der abgeleiteten Vorlage aus zugegriffen werden kann.

Die obige Typdefinition funktioniert nicht, da Sie keine Typdefinition für eine nicht angegebene Vorlage erstellen können. Dies funktioniert jedoch (und C++11 bietet native Unterstützung für Template-Typedefs):

template <typename VALUE>
struct derived_interface_type {
    typedef typename interface<derived, VALUE> type;
};

typedef typename derived_interface_type<int>::type derived_t;

Leider benötigen Sie für jede Instanziierung des abgeleiteten Templates einen derived_interface_type, es sei denn, es gibt einen anderen Trick, den ich noch nicht gelernt habe.

  • Ich brauchte genau diese Lösung für einen Code (danke!). Obwohl es funktioniert, verstehe ich nicht, wie die Vorlagenklasse derived kann ohne seine Template-Argumente verwendet werden, dh die Zeile typedef typename interface<derived, VALUE> type;

    – Karlton

    26. Januar 2018 um 16:13 Uhr

  • @Carlton es funktioniert im Grunde, weil der entsprechende zu füllende Vorlagenparameter als a definiert ist template <typename>. In gewisser Weise können Sie sich die Vorlagenparameter als einen „Metatyp“ vorstellen; der normale Metatyp für einen Vorlagenparameter ist typename was bedeutet, dass es mit einem regulären Typ gefüllt werden muss; der template Metatyp bedeutet, dass es mit einem Verweis auf eine Vorlage gefüllt werden muss. derived definiert eine Vorlage, die eine akzeptiert typename metatypisierter Parameter, so dass er in die Rechnung passt und hier referenziert werden kann. Sinn ergeben?

    – Mark McKenna

    27. Januar 2018 um 23:03 Uhr


  • C++11 noch immer typedef. Außerdem können Sie das Duplikat vermeiden int in Ihrem ersten Beispiel, indem Sie ein Standardkonstrukt wie a verwenden value_type im Typ DERIVED.

    – rubenvb

    19. Juni 2018 um 7:37 Uhr

  • Diese Antwort zielt nicht wirklich auf C++11 ab; Ich habe auf C++11 verwiesen, nur um zu sagen, dass Sie das umgehen können typedef Problem aus Block 2. Aber Punkt 2 ist gültig, denke ich … ja, das wäre wahrscheinlich ein einfacherer Weg, dasselbe zu tun.

    – Mark McKenna

    20. Juni 2018 um 14:03 Uhr

1647153020 522 Was sind einige Verwendungen von Template Template Parametern
oho

Darauf bin ich gestoßen:

template<class A>
class B
{
  A& a;
};

template<class B>
class A
{
  B b;
};

class AInstance : A<B<A<B<A<B<A<B<... (oh oh)>>>>>>>>
{

};

Kann gelöst werden zu:

template<class A>
class B
{
  A& a;
};

template< template<class> class B>
class A
{
  B<A> b;
};

class AInstance : A<B> //happy
{

};

oder (Arbeitscode):

template<class A>
class B
{
public:
    A* a;
    int GetInt() { return a->dummy; }
};

template< template<class> class B>
class A
{
public:
    A() : dummy(3) { b.a = this; }
    B<A> b;
    int dummy;
};

class AInstance : public A<B> //happy
{
public:
    void Print() { std::cout << b.GetInt(); }
};

int main()
{
    std::cout << "hello";
    AInstance test;
    test.Print();
}

  • Ich brauchte genau diese Lösung für einen Code (danke!). Obwohl es funktioniert, verstehe ich nicht, wie die Vorlagenklasse derived kann ohne seine Template-Argumente verwendet werden, dh die Zeile typedef typename interface<derived, VALUE> type;

    – Karlton

    26. Januar 2018 um 16:13 Uhr

  • @Carlton es funktioniert im Grunde, weil der entsprechende zu füllende Vorlagenparameter als a definiert ist template <typename>. In gewisser Weise können Sie sich die Vorlagenparameter als einen „Metatyp“ vorstellen; der normale Metatyp für einen Vorlagenparameter ist typename was bedeutet, dass es mit einem regulären Typ gefüllt werden muss; der template Metatyp bedeutet, dass es mit einem Verweis auf eine Vorlage gefüllt werden muss. derived definiert eine Vorlage, die eine akzeptiert typename metatypisierter Parameter, so dass er in die Rechnung passt und hier referenziert werden kann. Sinn ergeben?

    – Mark McKenna

    27. Januar 2018 um 23:03 Uhr


  • C++11 noch immer typedef. Außerdem können Sie das Duplikat vermeiden int in Ihrem ersten Beispiel, indem Sie ein Standardkonstrukt wie a verwenden value_type im Typ DERIVED.

    – rubenvb

    19. Juni 2018 um 7:37 Uhr

  • Diese Antwort zielt nicht wirklich auf C++11 ab; Ich habe auf C++11 verwiesen, nur um zu sagen, dass Sie das umgehen können typedef Problem aus Block 2. Aber Punkt 2 ist gültig, denke ich … ja, das wäre wahrscheinlich ein einfacherer Weg, dasselbe zu tun.

    – Mark McKenna

    20. Juni 2018 um 14:03 Uhr

1647153020 495 Was sind einige Verwendungen von Template Template Parametern
imallett

Hier ist eine Verallgemeinerung von etwas, das ich gerade benutzt habe. Ich poste es, weil es a ist sehr einfaches Beispiel und zeigt einen praktischen Anwendungsfall zusammen mit Standardargumenten:

#include <vector>

template <class T> class Alloc final { /*...*/ };

template <template <class T> class allocator=Alloc> class MyClass final {
  public:
    std::vector<short,allocator<short>> field0;
    std::vector<float,allocator<float>> field1;
};

  • Ich bin kürzlich auch auf diesen Anwendungsfall gestoßen, als ich mich darauf vorbereitete, meinen eigenen STL-kompatiblen Container zu schreiben, aber sehen Sie sich diesen Thread und die entsprechenden Antworten an, warum dies nicht der Ansatz ist, den die Standardbibliothek tatsächlich verfolgt (TL; DR – es bedeutet, dass dies nicht möglich ist). Aufrufer, einen Zuordner zu übergeben, der mehr als einen Vorlagenparameter akzeptiert): stackoverflow.com/questions/12362363/…

    – Saxbophon

    8. August 2020 um 22:38 Uhr

996240cookie-checkWas sind einige Verwendungen von Template-Template-Parametern?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy