2014-12-11 9 views
6

Ich versuche, operator T() mit SFINAE überladen, um eine Kopie zurückzugeben, wenn T ein grundlegender Typ ist, und eine const-Referenz, wenn T eine Klasse ist.Aktivieren Sie Konvertierungsoperator mit SFINAE

Bei der Verwendung eines double in meinem Beispiel unten, kann ich nicht die 2. Überlast (mit std::is_class) entfernt werden.

Das heißt, der Fehler Ich erhalte ist:

error: no type named ‘type’ in ‘struct std::enable_if<false, const double&>’ 
operator typename std::enable_if< std::is_class<T>::value, const T&>::type() const 
^ 

Was mache ich falsch?

#include <iostream> 
#include <type_traits> 

template<typename T> 
struct Foo 
{ 
    operator typename std::enable_if<!std::is_class<T>::value, T >::type() const 
    { 
     return _val; 
    } 

    operator typename std::enable_if< std::is_class<T>::value, const T&>::type() const 
    { 
     return _val; 
    } 

    T _val; 
}; 

int main() 
{ 
    Foo<double> f1; 
    f1._val = 0.3; 

    double d = f1; 
    std::cout << d << std::endl; 
    return 0; 
} 

Antwort

9

T wird bereits zum Zeitpunkt Ihrer Klasse Member-Funktionen instanziiert sind bekannt, so dass keine Substitution stattfindet, und statt SFINAE, erhalten Sie einen harten Fehler. Die einfachste Problemumgehung besteht darin, einen Dummy-Schablonenparameter für diese Überladungen des Operators einzuführen und ihn auf T zu setzen, damit die Typableitung immer noch auftreten kann.

template<typename U = T> 
operator typename std::enable_if<!std::is_class<U>::value, U >::type() const 
{ 
    return _val; 
} 

template<typename U = T> 
operator typename std::enable_if< std::is_class<U>::value, const U&>::type() const 
{ 
    return _val; 
} 

Live demo

+0

Für ein etwas anderes Beispiel und weitere Details, siehe http://StackOverflow.com/Questions/18100297/How-Cani-I-use-Stdenable-Fi-in-Aconversion-Operator – Asher

+0

@Assher Verwendung von SFINAE im Standard Das Template-Argument funktioniert in der Frage, mit der Sie verlinkt sind, aber in der obigen Frage, wo das OP versucht, es zu verwenden, um zwei sich gegenseitig ausschließende Überladungen zu definieren, wird es nicht [wie hier erklärt] (http://stackoverflow.com)/a/29502338/241631). – Praetorian

-2

Du sollst Definition von std :: lesen enable_if

Vorlage < Bool B, Klasse T = Leere> struct enable_if;

"Wenn B wahr ist, hat std :: enable_if einen öffentlichen Typ typedef, gleich T; andernfalls gibt es kein Element typedef."

+2

'enable_if' für SFINAE ausgelegt ist (siehe [Beispiele] (http://en.cppreference.com/w/cpp/types/enable_if) so hier das Problem ist nicht, dass typedef ist nicht hier, aber das SFINAE funktioniert nicht wie erwartet – Johan

+0

@Johan, was meinst du "die SFINAE funktioniert nicht wie erwartet?" Es wählt perfekt die zweite Operatordefinition (aufgrund 'const T &' Qualifikationsmerkmal), erste Template-Parameter von der enable_if in diesem moment ist 'false', du bekommst keinen' type' definiert, ende der story –

+2

Siehe diese [antwort] (http://stackoverflow.com/a/27433342/2439734). – Johan

3

Auch wenn es nicht die Lösung des Problems, warum der falsche Betreiber nicht verworfen wurde, die besondere Frage auf der Hand zu lösen, das heißt, durch const ref zurückzukehren für Klassentypen oder von Wert für andere kann eine Lösung gefunden werden mit std::conditional.

template< bool B, class T, class F > 
struct conditional; 

Stellt typedef artiges Element, das als T definiert ist, wenn B wahr bei der Kompilierung ist, oder als F wenn B falsch ist.

Arbeitsbeispiel:

#include <iostream> 
#include <type_traits> 

template<typename T> 
struct Foo 
{ 
    operator typename std::conditional< 
     std::is_class<T>::value, const T&, T>::type() const 
    { 
     return _val; 
    } 

    T _val; 
}; 

int main() 
{ 
    Foo<double> f1; 
    f1._val = 0.3; 

    double d = f1; 
    std::cout << d << std::endl; 
    return 0; 
} 
+1

Dies scheint eine anständige Möglichkeit, um eine Antwort auf Ihre Frage zu vermeiden, aber es ist erwähnenswert, dass es nicht verallgemeinert werden kann, wenn die Körper Ihres Betreibers nicht 100 sind % identisch – hvd

+0

@hvd Ich stimme zu. In diesem speziellen Fall, wo alles, was ich tun möchte, durch const ref für Klassentypen und durch Wert für andere Typen zurückgegeben wird, denke ich, dass die Verwendung von 'std :: conditional' ein einfacherer/kürzerer Ansatz als 2 ist std_enable_if' Überladungen. Würdest du zustimmen? –

+1

Ja, wenn die Körper der beiden Operatoren identisch sind, würde ich definitiv mit der 'std :: conditional' Lösung gehen. – Praetorian