C ++ - Vorlagen, die nur bestimmte Typen akzeptieren

158

In Java können Sie generische Klassen definieren, die nur Typen akzeptieren, die die Klasse Ihrer Wahl erweitern, z.

public class ObservableList<T extends List> {
  ...
}

Dies erfolgt mit dem Schlüsselwort "erweitert".

Gibt es ein einfaches Äquivalent zu diesem Schlüsselwort in C ++?

mgamer
quelle
Schon eine ziemlich alte Frage ... Ich glaube, was hier (auch in den Antworten) fehlt, ist, dass Java-Generika nicht wirklich ein Äquivalent zu Vorlagen in C ++ sind. Es gibt Ähnlichkeiten, aber imho sollte man vorsichtig sein, wenn man eine Java-Lösung direkt in C ++ übersetzt, um zu erkennen, dass sie möglicherweise für verschiedene Arten von Problemen gemacht sind;)
idclev 463035818

Antworten:

104

Ich schlage vor, die statische Assert- Funktion is_base_ofvon Boost in Verbindung mit der Boost Type Traits-Bibliothek zu verwenden:

template<typename T>
class ObservableList {
    BOOST_STATIC_ASSERT((is_base_of<List, T>::value)); //Yes, the double parentheses are needed, otherwise the comma will be seen as macro argument separator
    ...
};

In einigen anderen, einfacheren Fällen können Sie eine globale Vorlage einfach vorwärts deklarieren, sie jedoch nur für die gültigen Typen definieren (explizit oder teilweise spezialisieren):

template<typename T> class my_template;     // Declare, but don't define

// int is a valid type
template<> class my_template<int> {
    ...
};

// All pointer types are valid
template<typename T> class my_template<T*> {
    ...
};

// All other types are invalid, and will cause linker error messages.

[Minor EDIT 6/12/2013: Die Verwendung einer deklarierten, aber nicht definierten Vorlage führt zu Linker- und nicht zu Compiler-Fehlermeldungen.]

j_random_hacker
quelle
Statische Aussagen sind auch nett. :)
Macbirdie
5
@ John: Ich fürchte, die Spezialisierung würde nur myBaseTypegenau passen . Bevor Sie Boost schließen, sollten Sie wissen, dass das meiste davon nur Header-Vorlagencode ist. Daher fallen zur Laufzeit keine Speicher- oder Zeitkosten für Dinge an, die Sie nicht verwenden. Auch die speziellen Dinge, die Sie hier ( BOOST_STATIC_ASSERT()und is_base_of<>) verwenden würden, können nur mit Deklarationen implementiert werden (dh ohne tatsächliche Definitionen von Funktionen oder Variablen), sodass sie weder Platz noch Zeit benötigen.
j_random_hacker
50
C ++ 11 ist gekommen. Jetzt können wir verwenden static_assert(std::is_base_of<List, T>::value, "T must extend list").
Siyuan Ren
2
Übrigens ist die doppelte Klammer erforderlich, weil BOOST_STATIC_ASSERT ein Makro ist und die zusätzliche Klammer verhindert, dass der Präprozessor das Komma in den Funktionsargumenten is_base_of als zweites Makroargument interpretiert.
jfritz42
1
@Andreyua: Ich verstehe nicht wirklich, was fehlt. Sie können versuchen, eine Variable zu deklarieren my_template<int> x;oder zu my_template<float**> y;überprüfen, ob der Compiler diese zulässt, und dann eine Variable deklarieren my_template<char> z;und überprüfen, ob dies nicht der Fall ist.
j_random_hacker
134

Dies ist in C ++ normalerweise nicht gerechtfertigt, wie andere Antworten hier festgestellt haben. In C ++ neigen wir dazu, generische Typen basierend auf anderen Einschränkungen als "erbt von dieser Klasse" zu definieren. Wenn Sie das wirklich wollten, ist es in C ++ 11 ganz einfach und <type_traits>:

#include <type_traits>

template<typename T>
class observable_list {
    static_assert(std::is_base_of<list, T>::value, "T must inherit from list");
    // code here..
};

Dies bricht jedoch viele der Konzepte, die Menschen in C ++ erwarten. Es ist besser, Tricks wie das Definieren eigener Merkmale anzuwenden. observable_listMöchte beispielsweise einen beliebigen Containertyp akzeptieren, der die typedefs const_iteratorund eine beginund endmember-Funktion hat, die zurückgibt const_iterator. Wenn Sie dies auf Klassen beschränken, die von listeinem Benutzer erben , der einen eigenen Typ hat, der nicht erbt, listaber diese Mitgliedsfunktionen und typedefs bereitstellt, kann er Ihren nicht verwendenobservable_list .

Es gibt zwei Lösungen für dieses Problem. Eine davon besteht darin, nichts einzuschränken und sich auf das Tippen von Enten zu verlassen. Ein großer Nachteil dieser Lösung ist, dass sie eine große Menge an Fehlern enthält, die für Benutzer schwer zu verstehen sind. Eine andere Lösung besteht darin, Merkmale zu definieren, um den bereitgestellten Typ zu beschränken, um die Schnittstellenanforderungen zu erfüllen. Der große Nachteil dieser Lösung besteht darin, dass zusätzliches Schreiben erforderlich ist, was als ärgerlich angesehen werden kann. Die positive Seite ist jedoch, dass Sie Ihre eigenen Fehlermeldungen a la schreiben können static_assert.

Der Vollständigkeit halber wird die Lösung für das obige Beispiel angegeben:

#include <type_traits>

template<typename...>
struct void_ {
    using type = void;
};

template<typename... Args>
using Void = typename void_<Args...>::type;

template<typename T, typename = void>
struct has_const_iterator : std::false_type {};

template<typename T>
struct has_const_iterator<T, Void<typename T::const_iterator>> : std::true_type {};

struct has_begin_end_impl {
    template<typename T, typename Begin = decltype(std::declval<const T&>().begin()),
                         typename End   = decltype(std::declval<const T&>().end())>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

template<typename T>
struct has_begin_end : decltype(has_begin_end_impl::test<T>(0)) {};

template<typename T>
class observable_list {
    static_assert(has_const_iterator<T>::value, "Must have a const_iterator typedef");
    static_assert(has_begin_end<T>::value, "Must have begin and end member functions");
    // code here...
};

Im obigen Beispiel werden viele Konzepte gezeigt, die die Funktionen von C ++ 11 veranschaulichen. Einige Suchbegriffe für Neugierige sind verschiedene Vorlagen, SFINAE, Ausdruck SFINAE und Typmerkmale.

Rapptz
quelle
2
Ich habe bis heute nie bemerkt, dass C ++ - Vorlagen Enten-Typisierung verwenden. Irgendwie bizarr!
Andy
2
Angesichts der umfangreichen Richtlinienbeschränkungen, die C ++ in C eingeführt hat , ist nicht sicher, warum dies template<class T:list>ein so beleidigendes Konzept ist. Danke für den Tipp.
bvj
60

Die einfache Lösung, die noch niemand erwähnt hat, besteht darin, das Problem einfach zu ignorieren. Wenn ich versuche, einen intals Vorlagentyp in einer Funktionsvorlage zu verwenden, die eine Containerklasse wie einen Vektor oder eine Liste erwartet, wird ein Kompilierungsfehler angezeigt. Grob und einfach, aber es löst das Problem. Der Compiler versucht, den von Ihnen angegebenen Typ zu verwenden. Wenn dies fehlschlägt, wird ein Kompilierungsfehler generiert.

Das einzige Problem dabei ist, dass die Fehlermeldungen, die Sie erhalten, schwierig zu lesen sind. Dies ist jedoch ein sehr üblicher Weg. Die Standardbibliothek ist voll von Funktions- oder Klassenvorlagen, die vom Vorlagentyp ein bestimmtes Verhalten erwarten und nichts tun, um die Gültigkeit der verwendeten Typen zu überprüfen.

Wenn Sie schönere Fehlermeldungen wünschen (oder wenn Sie Fälle abfangen möchten, die keinen Compilerfehler erzeugen, aber dennoch keinen Sinn ergeben), können Sie je nach Komplexität entweder die statische Zusicherung von Boost oder verwenden die Boost concept_check Bibliothek.

Mit einem aktuellen Compiler haben Sie ein eingebautes_in static_assert, das stattdessen verwendet werden könnte.

jalf
quelle
7
Ja, ich habe immer gedacht, dass Vorlagen der Enten-Eingabe in C ++ am nächsten kommen. Wenn es alle für eine Vorlage erforderlichen Elemente enthält, kann es in einer Vorlage verwendet werden.
@ John: Es tut mir leid, ich kann weder Kopf noch Zahl daraus machen. Welcher Typ ist Tund woher heißt dieser Code? Ohne irgendeinen Kontext habe ich keine Chance, dieses Code-Snippet zu verstehen. Aber was ich gesagt habe, ist wahr. Wenn Sie versuchen, toString()einen Typ aufzurufen, der keine toStringMitgliedsfunktion hat, wird ein Kompilierungsfehler angezeigt.
Jalf
@ John: Das nächste Mal sollten Sie vielleicht ein bisschen weniger Trigger-fröhliche Downvoting-Leute sein, wenn das Problem in Ihrem Code ist
Jalf
@ Jalf, ok. +1. Dies war eine großartige Antwort, nur um das Beste daraus zu machen. Entschuldigung für das falsche Lesen. Ich dachte, wir sprachen über die Verwendung des Typs als Parameter für Klassen, nicht für Funktionsvorlagen, die vermutlich Mitglieder der ersteren sind, aber aufgerufen werden müssen, damit der Compiler sie markiert.
John
13

Wir können verwenden std::is_base_ofund std::enable_if:
( static_assertkann entfernt werden, die oben genannten Klassen können benutzerdefiniert implementiert oder aus Boost verwendet werden, wenn wir nicht referenzieren können type_traits)

#include <type_traits>
#include <list>

class Base {};
class Derived: public Base {};

#if 0   // wrapper
template <class T> class MyClass /* where T:Base */ {
private:
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
    typename std::enable_if<std::is_base_of<Base, T>::value, T>::type inner;
};
#elif 0 // base class
template <class T> class MyClass: /* where T:Base */
    protected std::enable_if<std::is_base_of<Base, T>::value, T>::type {
private:
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
};
#elif 1 // list-of
template <class T> class MyClass /* where T:list<Base> */ {
    static_assert(std::is_base_of<Base, typename T::value_type>::value , "T::value_type is not derived from Base");
    typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type base; 
    typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type::value_type value_type;

};
#endif

int main() {
#if 0   // wrapper or base-class
    MyClass<Derived> derived;
    MyClass<Base> base;
//  error:
    MyClass<int> wrong;
#elif 1 // list-of
    MyClass<std::list<Derived>> derived;
    MyClass<std::list<Base>> base;
//  error:
    MyClass<std::list<int>> wrong;
#endif
//  all of the static_asserts if not commented out
//  or "error: no type named ‘type’ in ‘struct std::enable_if<false, ...>’ pointing to:
//  1. inner
//  2. MyClass
//  3. base + value_type
}
firda
quelle
13

Soweit ich weiß, ist dies derzeit in C ++ nicht möglich. Es ist jedoch geplant, dem neuen C ++ 0x-Standard eine Funktion namens "Konzepte" hinzuzufügen, die die von Ihnen gesuchte Funktionalität bietet. Dieser Wikipedia-Artikel über C ++ - Konzepte wird ausführlicher erläutert.

Ich weiß, dass dies Ihr unmittelbares Problem nicht behebt, aber es gibt einige C ++ - Compiler, die bereits damit begonnen haben, Funktionen aus dem neuen Standard hinzuzufügen. Daher ist es möglicherweise möglich, einen Compiler zu finden, der die Konzeptfunktion bereits implementiert hat.

Barry Carr
quelle
4
Konzepte wurden leider aus dem Standard gestrichen.
Macbirdie
4
Einschränkungen und Konzepte sollten für C ++ 20 übernommen werden.
Petr Javorik
Es ist auch ohne Konzepte möglich, mit static_assertund SFINAE, wie die anderen Antworten zeigen. Das verbleibende Problem für jemanden, der aus Java oder C # oder Haskell (...) stammt, ist, dass der C ++ 20-Compiler keine Definitionsprüfung anhand der erforderlichen Konzepte durchführt, die Java und C # ausführen.
user7610
10

Ich denke, alle vorherigen Antworten haben den Wald vor lauter Bäumen aus den Augen verloren.

Java-Generika sind nicht dasselbe wie Vorlagen . Sie verwenden die Typlöschung , bei der es sich um eine dynamische Technik handelt , und nicht den Kompilierungszeitpolymorphismus , bei dem es sich um eine statische Technik handelt . Es sollte offensichtlich sein, warum diese beiden sehr unterschiedlichen Taktiken nicht gut zusammenpassen.

Anstatt zu versuchen, ein Kompilierungszeitkonstrukt zu verwenden, um eine Laufzeitkonstruktion zu simulieren, schauen wir uns an, was extendstatsächlich funktioniert: Laut Stack Overflow und Wikipedia wird Extended verwendet, um Unterklassen anzuzeigen.

C ++ unterstützt auch Unterklassen.

Sie zeigen auch eine Containerklasse an, die die Typlöschung in Form eines Generikums verwendet und erweitert wird, um eine Typprüfung durchzuführen. In C ++ müssen Sie die Löschmaschinerie selbst ausführen, was einfach ist: Erstellen Sie einen Zeiger auf die Oberklasse.

Lassen Sie es uns in ein typedef einwickeln, um die Verwendung zu vereinfachen, anstatt eine ganze Klasse zu erstellen, et voila:

typedef std::list<superclass*> subclasses_of_superclass_only_list;

Beispielsweise:

class Shape { };
class Triangle : public Shape { };

typedef std::list<Shape*> only_shapes_list;
only_shapes_list shapes;

shapes.push_back(new Triangle()); // Works, triangle is kind of shape
shapes.push_back(new int(30)); // Error, int's are not shapes

Nun scheint List eine Schnittstelle zu sein, die eine Art Sammlung darstellt. Eine Schnittstelle in C ++ wäre lediglich eine abstrakte Klasse, dh eine Klasse, die nur reine virtuelle Methoden implementiert. Mit dieser Methode können Sie Ihr Java-Beispiel problemlos in C ++ implementieren, ohne Konzepte oder Vorlagenspezialisierungen. Aufgrund der Suche nach virtuellen Tabellen ist die Leistung auch so langsam wie bei Generika im Java-Stil. Dies kann jedoch häufig ein akzeptabler Verlust sein.

Alice
quelle
3
Ich bin kein Fan von Antworten, die Sätze wie "es sollte offensichtlich sein" oder "jeder weiß" verwenden und dann erklären, was offensichtlich oder allgemein bekannt ist. Offensichtlich ist relativ zu Kontext, Erfahrung und Kontext der Erfahrung. Solche Aussagen sind von Natur aus unhöflich.
3Dave
2
@DavidLively Es ist ungefähr zwei Jahre zu spät, um diese Antwort wegen Etikette zu kritisieren, aber ich stimme Ihnen auch in diesem speziellen Fall nicht zu. Ich erklärte, warum diese beiden Techniken nicht zusammenpassen, bevor ich feststellte, dass es offensichtlich war, nicht danach. Ich lieferte den Kontext und sagte dann, dass die Schlussfolgerung aus diesem Kontext offensichtlich sei. Das passt nicht genau in deine Form.
Alice
Der Autor dieser Antwort sagte, dass etwas offensichtlich war, nachdem er ein schweres Heben durchgeführt hatte. Ich glaube nicht, dass der Autor sagen wollte, dass die Lösung offensichtlich war.
Luke Gehorsam
10

Ein Äquivalent, das nur vom Typ List abgeleitete Typen T akzeptiert, sieht aus

template<typename T, 
         typename std::enable_if<std::is_base_of<List, T>::value>::type* = nullptr>
class ObservableList
{
    // ...
};
nh_
quelle
8

Zusammenfassung: Tu das nicht.

Die Antwort von j_random_hacker zeigt Ihnen, wie das geht. Ich möchte jedoch auch darauf hinweisen, dass Sie dies nicht tun sollten . Der springende Punkt bei Vorlagen ist, dass sie jeden kompatiblen Typ akzeptieren können, und Einschränkungen des Java-Stiltyps brechen dies.

Javas Typeinschränkungen sind ein Fehler, keine Funktion. Sie sind vorhanden, weil Java die Löschung von Generika durchführt, sodass Java nicht herausfinden kann, wie Methoden allein anhand des Werts der Typparameter aufgerufen werden.

C ++ hat dagegen keine solche Einschränkung. Vorlagenparametertypen können beliebige Typen sein, die mit den Operationen kompatibel sind, mit denen sie verwendet werden. Es muss keine gemeinsame Basisklasse geben. Dies ähnelt Pythons "Duck Typing", wird jedoch zur Kompilierungszeit ausgeführt.

Ein einfaches Beispiel für die Leistungsfähigkeit von Vorlagen:

// Sum a vector of some type.
// Example:
// int total = sum({1,2,3,4,5});
template <typename T>
T sum(const vector<T>& vec) {
    T total = T();
    for (const T& x : vec) {
        total += x;
    }
    return total;
}

Diese Summenfunktion kann einen Vektor eines beliebigen Typs summieren, der die korrekten Operationen unterstützt. Es funktioniert sowohl mit Grundelementen wie int / long / float / double als auch mit benutzerdefinierten numerischen Typen, die den Operator + = überladen. Sie können diese Funktion sogar zum Verbinden von Zeichenfolgen verwenden, da diese + = unterstützen.

Es ist kein Ein- und Auspacken von Grundelementen erforderlich.

Beachten Sie, dass mit T () auch neue Instanzen von T erstellt werden. Dies ist in C ++ unter Verwendung impliziter Schnittstellen trivial, in Java mit Typbeschränkungen jedoch nicht wirklich möglich.

C ++ - Vorlagen haben zwar keine expliziten Typbeschränkungen, sind jedoch typsicher und werden nicht mit Code kompiliert, der die korrekten Vorgänge nicht unterstützt.

Catphive
quelle
2
Wenn Sie vorschlagen, sich niemals auf Vorlagen zu spezialisieren, können Sie auch erklären, warum diese in der Sprache vorliegen?
1
Ich verstehe, aber wenn Ihr Vorlagenargument von einem bestimmten Typ abgeleitet werden muss, ist es besser, eine einfach zu interpretierende Nachricht von static_assert zu haben, als das normale Erbrechen des Compilerfehlers.
jhoffman0x
1
Ja, C ++ ist hier ausdrucksstärker, aber während dies im Allgemeinen eine gute Sache ist (weil wir mit weniger mehr ausdrücken können), möchten wir manchmal die Leistung, die wir uns selbst geben, bewusst einschränken, um die Gewissheit zu erlangen, dass wir ein System vollständig verstehen.
j_random_hacker
Die Spezialisierung auf @Curg-Typen ist nützlich, wenn Sie einige Vorteile nutzen möchten, die nur für bestimmte Typen möglich sind. Zum Beispiel ist ein Boolescher Wert ~ normalerweise ~ jeweils ein Byte, obwohl ein Byte ~ normalerweise ~ 8 Bits / Boolescher Wert enthalten kann. Eine Vorlagensammlungsklasse kann (und im Fall von std :: map) auf Boolesche Werte spezialisiert werden, damit die Daten enger gepackt werden können, um Speicherplatz zu sparen.
Thecoshman
Zur Verdeutlichung lautet diese Antwort nicht "Vorlagen niemals spezialisieren", sondern "Verwenden Sie diese Funktion nicht, um zu begrenzen, welche Typen mit einer Vorlage verwendet werden können."
Thecoshman
6

Dies ist in einfachem C ++ nicht möglich, aber Sie können Vorlagenparameter zur Kompilierungszeit durch Konzeptprüfung überprüfen, z. B. mithilfe der BCCL von Boost .

Ab C ++ 20 werden Konzepte zu einem offiziellen Merkmal der Sprache.

Macbirdie
quelle
2
Nun, es ist möglich, aber die Konzeptprüfung ist immer noch eine gute Idee. :)
j_random_hacker
Ich meinte eigentlich, dass es in "normalem" C ++ nicht möglich war. ;)
Macbirdie
5
class Base
{
    struct FooSecurity{};
};

template<class Type>
class Foo
{
    typename Type::FooSecurity If_You_Are_Reading_This_You_Tried_To_Create_An_Instance_Of_Foo_For_An_Invalid_Type;
};

Stellen Sie sicher, dass abgeleitete Klassen die FooSecurity-Struktur erben und der Compiler an den richtigen Stellen verärgert wird.

Stuart
quelle
@Zehelvion Type::FooSecuritywird in der Vorlagenklasse verwendet. Wenn die im Vorlagenargument übergebene Klasse dies nicht FooSecuritygetan hat , führt der Versuch, sie zu verwenden, zu einem Fehler. Es ist sicher, dass eine Klasse, die im Vorlagenargument übergeben wurde, keine FooSecurity hat, von der sie nicht abgeleitet ist Base.
GingerPlusPlus
2

Verwendung des C ++ 20-Konzepts

https://en.cppreference.com/w/cpp/language/constraints cppreference gibt den Anwendungsfall für die Vererbung als explizites Konzeptbeispiel an:

template <class T, class U>
concept Derived = std::is_base_of<U, T>::value;
 
template<Derived<Base> T>
void f(T);  // T is constrained by Derived<T, Base>

Für mehrere Basen wird die Syntax vermutlich wie folgt lauten:

template <class T, class U, class V>
concept Derived = std::is_base_of<U, T>::value || std::is_base_of<V, T>::value;
 
template<Derived<Base1, Base2> T>
void f(T);

GCC 10 scheint es implementiert zu haben: https://gcc.gnu.org/gcc-10/changes.html und Sie können es als PPA unter Ubuntu 20.04 erhalten . https://godbolt.org/ Mein lokales GCC 10.1 hat es noch nicht erkannt concept, ist sich also nicht sicher, was los ist.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
1

Gibt es ein einfaches Äquivalent zu diesem Schlüsselwort in C ++?

Nein.

Je nachdem, was Sie erreichen möchten, gibt es möglicherweise geeignete (oder sogar bessere) Ersatzprodukte.

Ich habe einen STL-Code durchgesehen (unter Linux denke ich, dass er aus der Implementierung von SGI stammt). Es hat "Konzeptbehauptungen"; Wenn Sie beispielsweise einen Typ benötigen, der *xund versteht ++x, enthält die Konzeptzusicherung diesen Code in einer Nichtstun-Funktion (oder ähnlichem). Es erfordert etwas Overhead, daher ist es möglicherweise klug, es in ein Makro einzufügen, dessen Definition davon abhängt #ifdef debug.

Wenn die Unterklassenbeziehung wirklich das ist, worüber Sie wissen möchten, können Sie dies im Konstruktor behaupten T instanceof list(außer, dass sie in C ++ anders "geschrieben" ist). Auf diese Weise können Sie Ihren Weg aus dem Compiler testen, ohne ihn für Sie überprüfen zu können.

Jonas Kölker
quelle
1

Es gibt kein Schlüsselwort für solche Typprüfungen, aber Sie können Code einfügen, der zumindest ordnungsgemäß fehlschlägt:

(1) Wenn eine Funktionsvorlage nur Parameter einer bestimmten Basisklasse X akzeptieren soll, weisen Sie sie einer X-Referenz in Ihrer Funktion zu. (2) Wenn Sie Funktionen akzeptieren möchten, aber keine Grundelemente oder umgekehrt, oder wenn Sie Klassen auf andere Weise filtern möchten, rufen Sie eine (leere) Vorlagenhilfefunktion in Ihrer Funktion auf, die nur für die Klassen definiert ist, die Sie akzeptieren möchten.

Sie können (1) und (2) auch in Elementfunktionen einer Klasse verwenden, um diese Typprüfungen für die gesamte Klasse zu erzwingen.

Sie können es wahrscheinlich in ein intelligentes Makro einfügen, um Ihre Schmerzen zu lindern. :) :)

Jaap
quelle
-2

Nun, Sie könnten Ihre Vorlage mit folgendem Inhalt erstellen:

template<typename T>
class ObservableList {
  std::list<T> contained_data;
};

Dadurch wird die Einschränkung jedoch implizit, und Sie können nicht einfach alles angeben, das wie eine Liste aussieht. Es gibt andere Möglichkeiten, die verwendeten Containertypen einzuschränken, z. B. durch Verwendung bestimmter Iteratortypen, die nicht in allen Containern vorhanden sind. Dies ist jedoch eher eine implizite als eine explizite Einschränkung.

Nach meinem besten Wissen gibt es im aktuellen Standard kein Konstrukt, das die Java-Anweisung in vollem Umfang widerspiegeln würde.

Es gibt Möglichkeiten, die Typen einzuschränken, die Sie in einer von Ihnen geschriebenen Vorlage verwenden können, indem Sie bestimmte Typedefs in Ihrer Vorlage verwenden. Dadurch wird sichergestellt, dass die Kompilierung der Vorlagenspezialisierung für einen Typ, der dieses bestimmte typedef nicht enthält, fehlschlägt, sodass Sie bestimmte Typen selektiv unterstützen / nicht unterstützen können.

In C ++ 11 sollte die Einführung von Konzepten dies vereinfachen, aber ich denke nicht, dass es genau das tut, was Sie möchten.

Timo Geusch
quelle