Wie überprüfe ich den Typ eines Vorlagenparameters?

91

Angenommen, ich habe eine Vorlagenfunktion und zwei Klassen

class animal {
}
class person {
}

template<class T>
void foo() {
  if (T is animal) {
    kill();
  }
}

Wie überprüfe ich, ob T ein Tier ist? Ich möchte nichts haben, das während der Laufzeit überprüft wird. Vielen Dank

WhatABeautifulWorld
quelle
54
Ich würde "pet" anstelle von "kill" setzen :-)
JimBamFeng

Antworten:

130

Verwendung is_same:

#include <type_traits>

template <typename T>
void foo()
{
    if (std::is_same<T, animal>::value) { /* ... */ }  // optimizable...
}

Normalerweise ist das jedoch ein völlig unbrauchbares Design, und Sie möchten sich wirklich spezialisieren :

template <typename T> void foo() { /* generic implementation  */ }

template <> void foo<animal>()   { /* specific for T = animal */ }

Beachten Sie auch, dass es ungewöhnlich ist, Funktionsvorlagen mit expliziten (nicht abgeleiteten) Argumenten zu haben. Es ist nicht ungewöhnlich, aber oft gibt es bessere Ansätze.

Kerrek SB
quelle
2
Danke! Tatsächlich teilen sie eine
Menge
3
@WhatABeautifulWorld: Sie können Ihren Code immer so faktorisieren, dass der typabhängige Teil in eine spezialisierbare Funktion verwiesen werden kann ...
Kerrek SB
1
Ein kurzes Follow-up: Wenn ich std :: is_same verwende, wird der Code für andere Vorlagenparameter NICHT verlangsamt, oder?
WhatABeautifulWorld
1
@WhatABeautifulWorld: Die Merkmalswerte sind alle statisch bekannt. Es sollten keine Laufzeitkosten anfallen, vorausgesetzt, Ihr Compiler ist halbwegs anständig. Überprüfen Sie im Zweifelsfall die Baugruppe.
Kerrek SB
2
@ AdriC.S.: Da Tnicht abgeleitet wird, können Sie nicht viel tun. Sie können die primäre Vorlage nicht implementieren und eine Spezialisierung erstellen oder eine statische Zusicherung mit hinzufügen is_same.
Kerrek SB
34

Ich denke heutzutage ist es besser zu verwenden, aber nur mit C ++ 17.

#include <type_traits>

template <typename T>
void foo() {
    if constexpr (std::is_same_v<T, animal>) {
        // use type specific operations... 
    } 
}

Wenn Sie einige typspezifische Operationen in if expression body without verwenden constexpr, wird dieser Code nicht kompiliert.

Константин Гудков
quelle
8
stattdessen std::is_same<T, U>::valuekönnten Sie kürzere verwenden:std::is_same_v<T, U>
Fureeish
9

In C ++ 17 können wir Varianten verwenden .

Zur Verwendung std::variantmüssen Sie den Header einschließen:

#include <variant>

Danach können Sie std::variantIhren Code wie folgt hinzufügen :

using Type = std::variant<Animal, Person>;

template <class T>
void foo(Type type) {
    if (std::is_same_v<type, Animal>) {
        // Do stuff...
    } else {
        // Do stuff...
    }
}
Edwin Pratt
quelle
8
Wie sind T und Typ verbunden?
Mabraham
4
Diese Antwort ist in mehrfacher Hinsicht problematisch. Außerdem sind die tatsächlichen Fehler ( typedas ist der Wert des Typs Typeoder eine Vorlage, die hier keinen Sinn is_same_vergibt ) im Kontext von nicht aussagekräftig variant. Das entsprechende "Merkmal" ist holds_alternative.
Pixelchemist
std::variantist hier völlig unnötig
tjysdsg
7

Sie können Ihre Vorlagen basierend auf den folgenden Parametern spezialisieren:

template <> void foo<animal> {

}

Beachten Sie, dass dadurch eine völlig neue Funktion erstellt wird, die auf dem Typ basiert, der als übergeben wird T. Dies ist normalerweise vorzuziehen, da es die Unordnung verringert und im Wesentlichen der Grund ist, warum wir überhaupt Vorlagen haben.

Schablonenjunge
quelle
Hmm. Ist diese Methode wirklich die einzig bevorzugte Methode, um Vorlagenargumente zu spezialisieren? Angenommen, ich habe 10 verschiedene untergeordnete Klassen, die ich innerhalb der Vorlagenfunktion verwalten muss. Muss ich wirklich 10 verschiedene Vorlagenfunktionen für die jeweilige Klasse schreiben? Ich denke, ich vermisse hier möglicherweise den Kernpunkt.
Volkan Güven
Das klingt aber wirklich nach einer guten Idee, wenn jemand type_traits nicht verwenden möchte. Wie bereits erwähnt, kann die Hauptlogik in einer anderen Funktion ausgeführt werden, die ein zusätzliches Flag zur Angabe des Typs akzeptiert. Diese spezielle Deklaration kann das Flag einfach entsprechend setzen und alle anderen Argumente direkt weitergeben, ohne etwas zu berühren. Wenn also 10 verschiedene Klassen behandelt werden müssen, sind es im Grunde 10 Zeilen für 10 verschiedene Funktionsdefinitionen. Dies wird jedoch sehr kompliziert, wenn mehr als eine Vorlagenvariable vorhanden ist.
Harish Ganesan