Ich habe ein Problem SFINAE :
Im folgenden Code möchte ich, dass der C ++ - Compiler den spezialisierten Funktor auswählt und "Spezial" druckt, aber stattdessen "allgemein" druckt.
%Vor% Wie kann ich es reparieren, so dass die spezialisierte Struktur automatisch verwendet wird? Hinweis Ich möchte die Functor
struct auf Foo
nicht direkt spezialisieren, aber ich möchte sie auf alle Typen mit dem Typ Vec
spezialisieren.
P.S .: Ich benutze g ++ 4.4.4
Es tut mir leid, Sie in der letzten Antwort irregeführt zu haben. Ich dachte einen Moment, es wäre einfacher. Also werde ich versuchen, hier eine vollständige Lösung zu bieten. Der allgemeine Ansatz zur Lösung dieser Art von Problemen besteht darin, eine Merkmale Hilfsvorlage zu schreiben und sie zusammen mit enable_if
(entweder C ++ 11, Boost oder manuelle Implementierung) zu verwenden, um eine Klassenspezialisierung zu bestimmen:
Merkmal
Ein einfacher Ansatz, nicht unbedingt der beste, aber einfach zu schreiben wäre:
%Vor% Der Ansatz ist einfach, bietet zwei Vorlagenfunktionen, die Typen unterschiedlicher Größe zurückgeben. Eine davon nimmt den verschachtelten Vec
-Typ und der andere nimmt Ellipsen an. Für alle Typen, die eine verschachtelte Vec
haben, ist die erste Überladung eine bessere Übereinstimmung (Ellipse ist die schlechteste Übereinstimmung für jeden Typ). Für die Typen, die kein verschachteltes Vec
SFINAE haben, wird diese Überladung verworfen und die einzige übriggebliebene Option ist die Ellipse. Jetzt müssen wir uns fragen, ob ein Typ einen verschachtelten Vec
-Typ hat.
Aktivieren Sie if
Sie können dies aus jeder Bibliothek verwenden, oder Sie können Ihre eigenen rollen, es ist ganz einfach:
%Vor% Wenn das erste Argument false
ist, ist die Basisvorlage die einzige Option, die keine verschachtelte type
hat, wenn die Bedingung true
ist, dann hat enable_if
eine verschachtelte type
das wir können mit SFINAE verwenden.
Implementierung
Nun müssen wir die Vorlage und die Spezialisierung angeben, die SFINAE nur für die Typen mit einem verschachtelten Vec
verwenden:
Immer wenn wir Functor
mit einem Typ instanziieren, versucht der Compiler, die Spezialisierung zu verwenden, die wiederum has_nested_Vec
instanziiert und einen Wahrheitswert erhält, der an enable_if
übergeben wird. Für die Typen, für die der Wert false
ist, hat enable_if
keinen verschachtelten type
-Typ, sodass die Spezialisierung in SFINAE verworfen wird und die Basisvorlage verwendet wird.
Ihr besonderer Fall
In Ihrem speziellen Fall, wo es scheint, dass Sie nicht wirklich den ganzen Typ, sondern nur den Operator spezialisieren müssen, können Sie die drei Elemente in einem einzigen mischen: a Functor
, das zu einem von zwei internen Elementen führt Template-Funktionen basierend auf dem Vorhandensein von Vec
, wodurch die Notwendigkeit von enable_if
und der Merkmalsklasse:
Auch wenn dies eine alte Frage ist, denke ich, es lohnt sich immer noch, ein paar mehr Alternativen zu geben, um den ursprünglichen Code schnell zu reparieren.
Grundsätzlich ist das Problem nicht mit der Verwendung von SFINAE (dieser Teil ist in Ordnung, eigentlich), aber mit der Übereinstimmung der Standardparameter in der primären Vorlage ( void
) mit dem Argument in der partiellen Spezialisierung (% Code%). Aufgrund des Standardparameters in der primären Vorlage bedeutet typename T::Vec
tatsächlich Functor<Foo>
. Wenn der Compiler versucht, das mit der Spezialisierung zu instanziieren, versucht er, die beiden Argumente mit denen in der Spezialisierung abzugleichen und schlägt fehl, da Functor<Foo, void>
nicht für void
ersetzt werden kann. Es greift dann auf die Verwendung der primären Vorlage zurück.
Die schnellste Lösung, die davon ausgeht, dass alle Ihre std::vector<int>
s Vec
s sind, besteht darin, die Zeile
mit diesem
%Vor%Die Spezialisierung wird jetzt verwendet, weil die Argumente übereinstimmen. Einfach, aber zu einschränkend. Natürlich müssen wir den Typ des Arguments in der Spezialisierung besser steuern, damit es mit etwas übereinstimmt, das wir als Standardparameter in der primären Vorlage angeben können. Eine schnelle Lösung, die keine neuen Merkmale definieren muss, ist dies:
%Vor% Dies funktioniert für jeden std::vector<int>
-Typ, der hier sinnvoll sein könnte, einschließlich grundlegender Typen und Arrays und Referenzen oder Zeigern auf diese.
Eine weitere Alternative zum Ermitteln der Existenz eines Elementtyps ist die Verwendung von void_t
. Da gültige partielle Spezialisierungen der allgemeinen Implementierung vorzuziehen sind, solange sie den Standardparametern entsprechen, möchten wir einen Typ, der bei Gültigkeit als void
ausgewertet wird und nur gültig ist, wenn das angegebene Element existiert. Dieser Typ ist üblicherweise (und ab C ++ 17 kanonisch) als void_t
bekannt.
Wenn Ihr Compiler es nicht richtig unterstützt (in frühen C ++ 14-Compilern wurde nicht garantiert, dass unbenutzte Parameter in Alias-Vorlagen SFINAE sicherten, wodurch das obige void_t
durchbrochen wurde), ist eine Problemumgehung verfügbar.
Ab C ++ 17 ist void_t
in der Hilfsprogrammbibliothek in type_traits
verfügbar.
Damit ist die Ausgabe special
, wie vorgesehen.
In diesem Fall ist der Prozess sehr einfach, da wir nach der Existenz eines Mitgliedstyps suchen. Es kann ohne SFINAE-Ausdruck oder die type_traits
-Bibliothek ausgeführt werden, was es uns ermöglicht, die Überprüfung neu zu schreiben, um C ++ 03-Einrichtungen zu verwenden, falls erforderlich.
Nach meinem Wissen sollte dies mit den meisten, wenn nicht allen SFINAE-fähigen C ++ 03-, C ++ 11-, C ++ 14- oder C ++ 1z-kompatiblen Compilern funktionieren. Dies kann nützlich sein, wenn Sie mit Compilern arbeiten, die etwas hinter dem Standard zurückbleiben oder wenn Sie für Plattformen kompilieren, die noch keine C ++ 11-kompatiblen Compiler haben.
Weitere Informationen zu void_t
finden Sie unter cppreference .