2015-02-02 9 views
6

ich eine wenig Zug-Klasse implementieren will, um zu bestimmen, ob ein Typ operator() richtig überlastet, so dass ich einen Typen wie so abfragen:C++ 11: SFINAE in Template-Parametern, GCC vs Clang

FunctorCheck<F, void(int, char)>::value 

Ursprünglich hatte ich eine Idee, wie man das von this question implementieren kann, aber nachdem ich eine Cppcon lecture on TMP gesehen hatte, dämmerte es mir, dass dieses Problem viel eleganter gelöst werden konnte. Das ist, was ich kam mit, und diese kompiliert und läuft einwandfrei auf Clang 3.5.0:

template <typename ...> 
using void_t = void; 

template <typename Functor, typename ... Args> 
using FunctorReturn = decltype(declval<Functor>()(declval<Args>()...)); 

template <typename Functor, typename Signature, typename = void> 
struct FunctorCheck: public std::false_type 
{}; 

template <typename Functor, typename R, typename ... Args> 
struct FunctorCheck<Functor, R(Args...), 
    void_t<FunctorReturn<Functor, Args ...>> // SFINAE can kick in here 
> : public std::is_same<R, FunctorReturn<Functor, Args ...>>::type 
{}; 

Wie Sie vielleicht bemerkt haben, bin ich mit den vorgeschlagenen C++ 17 void_t SFINAE in der Vorlage zu nutzen Parameter der Spezialisierung. Wenn FunctorReturn einen Functor-Typ empfängt, der nicht mit der Argumentliste kompatibel ist, wird schlecht formatiert, SFINAE tritt ein und die nicht spezialisierte Version wird instanziiert. Wenn der erste Ausdruck wohlgeformt ist, vergleicht std::is_same die Rückgabetypen, um festzustellen, ob es eine Übereinstimmung gibt. In der oben erwähnten Vorlesung erwähnt Walter Brown, dass diese Technik vom ersten Tag an Clang funktioniert hat und nicht von Tag 1 an GCC bearbeitet hat, einfach weil sie verschiedene Implementierungen für etwas auswählten, das der Standard nicht spezifizieren konnte. Aber angesichts dieser Version ist so viel eleganter als was ich vorher hatte, gibt es etwas, was ich tun kann, um dies auf GCC (> = 4.9) zu kompilieren?

(Auch hat jemand eine Ahnung, wie diese auf den aktuellen Versionen von Visual Studio verhalten würde?)

+0

Er meinte 'template struct Verdränger verwenden {type = Leere verwendet;}; Template mit void_t = typenname voider :: type; 'statt plain' void_t' –

+0

Schön! Wenn du das als Antwort postest, akzeptiere ich (angenommen, dass es funktioniert) :-) – JorenHeit

+0

'std :: is_same' sieht falsch aus - du rufst über die Konvertierung auf, du solltest auch zurückkehren. 'std :: is_same {} || std :: is_convertible , R> {} 'wäre ein besserer Test für mich. – Yakk

Antwort

6

Das war CWG issue 1558. Der nicht spezifizierte Teil war Behandlung von unbenutzten Argumenten in einer Aliasvorlage. In GCC < 5.0 können die nicht verwendeten Argumente nicht zu einem Substitutionsfehler führen, daher void_t , und versucht, eine Klassenvorlagenspezialisierung zu instanziieren, was zu einem schwerwiegenden Fehler führt.

Eine Abhilfe für GCC (< 5.0) ist die folgende Umsetzung:

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

template <typename... Ts> 
using void_t = typename voider<Ts...>::type; 

DEMO