SFINAE: Der Compiler wählt die spezialisierte Template-Klasse nicht aus

8

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

    
Frank 04.07.2012, 00:13
quelle

3 Antworten

11

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:

%Vor%

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:

beseitigt wird %Vor%     
David Rodríguez - dribeas 04.07.2012, 02:26
quelle
2

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

zu ersetzen %Vor%

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.

    
bogdan 04.12.2014 23:38
quelle
1

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.

%Vor%

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.

%Vor%

Ab C ++ 17 ist void_t in der Hilfsprogrammbibliothek in type_traits verfügbar.

%Vor%

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.

%Vor%

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 .

    
Justin Time 27.11.2016 20:39
quelle

Tags und Links