In C++ 1z mit Konzepten lite, können Sie dies tun:
template<class T>
requires std::is_base_of<Foo, T>{}()
void foo(T arg) {
}
unter der aktuellen (experimentell) Umsetzung. Welches ist ziemlich sauber und klar. Es kann ein Weg sein, um so etwas wie:
template<derived_from<Foo> T>
void foo(T arg) {
}
aber ich habe es nicht geklappt hat. Sie können auf jeden Fall tun:
template<derived_from_foo T>
void foo(T arg){
}
, wo wir ein eigenes Konzept derived_from_foo
genannt haben, die Art gilt genau dann, wenn von foo
abgeleitet ist. Was ich nicht kann, sind Template-Konzepte - Konzepte, die aus Template-Typ-Parametern generiert werden.
In C++ 14, hier sind zwei Methoden. Zuerst normalen SFINAE:
template<class T,
class=std::enable_if_t<std::is_base_of<Foo, T>{}>
>
void foo(T arg) {
}
hier schaffen wir eine Vorlage, die den Typ T
von seinem Argument folgert. Es versucht dann, sein zweites Argument vom ersten Argument abzuleiten.
Das Argument des zweiten Typs hat keinen Namen (daher class=
), weil wir es nur für einen SFINAE-Test verwenden.
Der Test ist enable_if_t<condition>
. enable_if_t<condition>
generiert den Typ void
, wenn condition
wahr ist. Wenn condition
falsch ist, schlägt es in "dem unmittelbaren Kontext" fehl und erzeugt einen Substitutionsfehler.
SFINAE ist "Substitutionsfehler ist kein Fehler" - wenn Ihr Typ T
einen Fehler im "unmittelbaren Kontext" der Signatur der Funktionsvorlage generiert, generiert dies keinen Fehler bei der Kompilierung, sondern führt stattdessen dazu Die Funktionsvorlage wird in diesem Fall nicht als gültige Überladung betrachtet.
"Sofortiger Kontext" ist ein technischer Begriff hier, aber im Grunde bedeutet es, dass der Fehler "früh genug" sein muss, um abgefangen zu werden. Wenn es erforderlich ist, Körper von Funktionen zu kompilieren, um den Fehler zu finden, ist das nicht im "unmittelbaren Kontext".
Nun, dies ist nicht die einzige Möglichkeit. Ich persönlich mag es, meinen SFINAE-Code hinter einem Glanz der Anständigkeit zu verstecken. Im Folgenden verwende ich Tag woanders zu „verstecken“ das Scheitern Disposition, anstatt sie ganz vorne in der Funktion Signatur setzen:
template<class T>
struct tag {
using type=T;
constexpr tag(tag const&) = default;
constexpr tag() = default;
template<class U,
class=std::enable_if_t<std::is_base_of<T,U>{}>
>
constexpr tag(tag<U>) {}
};
struct Base{};
struct Derived:Base{};
template<class T>
void foo(T t, tag<Base> = tag<T>{}) {
}
wir hier eine tag
Versandart erstellen, und es ermöglicht die Konvertierung zur Basis. tag
können wir mit den Typen als Werte Wert, und verwenden Sie mehr normale C++ -Operationen auf ihnen (anstelle von Template-wie Metaprogrammierung <>
s überall).
Wir geben dann foo
ein zweites Argument vom Typ tag<Base>
, dann konstruieren Sie es mit einem tag<T>
. Dies kann nicht kompiliert werden, wenn T
kein abgeleiteter Typ von Base
ist.
live example.
Das Schöne an dieser Lösung ist, dass der Code, der es nicht funktioniert, intuitiver erscheint - tag<Unrelated>
kann nicht in tag<Base>
konvertiert werden. Dies verhindert jedoch nicht, dass die Funktion für eine Überladungsauflösung in Betracht gezogen wird, was ein Problem sein kann.
Ein Weg mit weniger Kesselblech ist:
template<class T>
void foo(T t, Base*=(T*)0) {
}
wo wir die Tatsache nutzen, dass Zeiger umgewandelt werden können iff eine Ableitungsbeziehung zwischen ihnen.
In C++ 11 (und ohne constexpr
Unterstützung), schreiben wir zunächst einen Helfer:
namespace notstd {
template<bool b, class T=void>
using enable_if_t=typename std::enable_if<b,T>::type;
}
dann:
template<class T,
class=notstd::enable_if_t<std::is_base_of<Foo, T>::value>
>
void foo(T arg) {
}
wenn der Helfer nicht mögen, wir bekommen dieses hässliche Extra:
template<class T,
class=typename std::enable_if<std::is_base_of<Foo, T>::value>::type
>
void foo(T arg) {
}
t Die obige zweite C++ 14-Technik kann auch in C++ 11 übersetzt werden.
Sie können einen Alias schreiben, die den Test nicht, wenn Sie wollen: [? Wie ich eine Template-Klasse, um bestimmte Typen beschränken]
template<class U>
using base_test=notstd::enable_if_t<std::is_base_of<Base, U>::value>;
template<class T,
class=base_test<T>
>
void foo(T arg) {
}
Related to (http://stackoverflow.com/ q/16976720/1708801) –
@ShafikYaghmour Beachten Sie, dass es sich nicht um ein Duplikat handelt. Ich möchte eine * Funktion * Vorlage einschränken, während die verknüpfte Frage sich auf eine * Klasse * Vorlage bezieht. – becko
@ShafikYaghmour Hätten Sie es nicht geschlossen, wenn es sich um ein exaktes Duplikat handelt und Sie eine Antwort auf das andere erhalten haben? – Barry