2012-06-02 9 views
6

Ich versuche Nichtmitglied Operatorfunktion Vorlagen wie zu schreiben:Wie verwendet man enable_if für sich gegenseitig ausschließende Nichtmitgliedsfunktionsvorlagen?

#include <utility> 

template < typename T, unsigned L > 
class MyType; 

template < typename T, typename U, unsigned L > 
auto operator ==(MyType<T,L> const &l, MyType<U,L> const &r) 
-> decltype(std::declval<T>() == std::declval<U>()) 
{ /*...*/ } 

Aber wenn ich zu behandeln versuchen, wenn l und r unterschiedliche Längen haben:

template < typename T, unsigned Lt, typename U, unsigned Lu, class Enable = typename std::enable_if<(Lt < Lu)>::type > 
auto operator ==(MyType<T,Lt> const &l, MyType<U,Lu> const &r) 
-> decltype(std::declval<T>() == std::declval<U>()) 
{ /*...*/ } 

template < typename T, unsigned Lt, typename U, unsigned Lu, class Enable = typename std::enable_if<(Lt > Lu)>::type > 
auto operator ==(MyType<T,Lt> const &l, MyType<U,Lu> const &r) 
-> decltype(std::declval<T>() == std::declval<U>()) 
{ /*...*/ } 

ich Mehrdeutigkeit Fehler bekommen. Ich habe versucht, so etwas wie:

template < typename T, unsigned Lt, typename U, unsigned Lu, bool B = (Lt < Lu), class Enable = typename std::enable_if<B>::type > 
auto operator ==(MyType<T,Lt> const &l, MyType<U,Lu> const &r) 
-> decltype(std::declval<T>() == std::declval<U>()) 
{ /*...*/ } 

template < typename T, unsigned Lt, typename U, unsigned Lu, bool B = (Lt > Lu), class Enable = typename std::enable_if<B>::type > 
auto operator ==(MyType<T,Lt> const &l, MyType<U,Lu> const &r) 
-> decltype(std::declval<T>() == std::declval<U>()) 
{ /*...*/ } 

, die ich (hier auf S. O.) gelesen haben für die Mitgliedsfunktionsschablonen Probleme wie diese zu lösen. (Manchmal haben die Befragten eine Mitgliederfunktion in eine Mitgliederfunktionsvorlage geändert, um dies zu aktivieren.) Aber die Fehler ändern sich für mich nicht. Muss ich zu enable_if in den Rückgabetyp wechseln?

Oh, der Ausdruck des Rückgabetyps soll diesen Operator ausschließen, wenn die beiden Elementtypen nicht verglichen werden können. Wird es tatsächlich funktionieren? Ist es kompatibel mit der enable_if dort auch?

+1

Was genau ist der Mehrdeutigkeitsfehler? –

Antwort

11

Interessanterweise schrieb eine certain fellow here on SO vor kurzem eine Blogpost, die eine nette C++ 11-style SFINAE-Technik zeigt, die leicht überladene Funktionen zulässt. Die Technik und Erklärung sind here zur Verfügung gestellt.

Kurz gesagt, Ihr Code schlägt fehl, weil beide Vorlagen, wenn sie zum ersten Mal geparst werden und wenn unabhängige Deklarationen aufgelöst werden, genau die gleichen, typenweise sind. Wie bei Standardfunktionsargumenten werden Standardschablonenargumente nur dann ersetzt, wenn die Funktion tatsächlich aufgerufen wird. Dies ist, was beide Vorlagen am Punkt der Erklärung an den Compiler wie folgt aussehen:

template<class T, unsigned Lt, class U, unsigned Lu, class Enable> 
auto operator==(MyType<T,Lt> const& l, MyType<U,Lu> const& r); 

Der folgende Code sollte erreichen, was Sie wollen:

namespace detail{ 
enum class enabler{}; 
} 

template<bool B, class T = detail::enabler> 
using EnableIf = typename std::enable_if<B, T>::type; 

template < typename T, unsigned Lt, typename U, unsigned Lu, EnableIf<(Lt < Lu)>...> 
auto operator ==(MyType<T,Lt> const &l, MyType<U,Lu> const &r) 
-> decltype(std::declval<T>() == std::declval<U>()) 
{ /*...*/ } 

template < typename T, unsigned Lt, typename U, unsigned Lu, EnableIf<(Lt > Lu)>...> 
auto operator ==(MyType<T,Lt> const &l, MyType<U,Lu> const &r) 
-> decltype(std::declval<T>() == std::declval<U>()) 
{ /*...*/ } 

Eine Frage bleibt aber ... was passieren soll, wenn Lt == Lu? In diesem Fall ist keine Überlastung möglich.

+0

Wow! Mein Verstand wurde durchgebrannt. Ich hatte über die Multi-Condition (leicht genug mit Variadic) nachgedacht, aber ... kombinierte so viele Techniken und schaffte es, einen solchen syntaktischen Sweet Spot zu treffen. Tolle. Danke, dass du den Artikel geteilt hast. –

+0

Die Version von 'operator ==' im ersten Block deckt den Fall ab, wenn die zweiten Parameter der Klassenvorlage gleich sind. Die zwei Versionen, nach denen ich frage, sind zusätzlich zur ersten Version und ersetzen sie nicht. – CTMacUser

+0

Das ist ein * sehr * interessanter Artikel. Für meinen Code habe ich 'typename std :: enable_if <(Lt < Lu)> :: type ...' als endgültigen Template-Parameter verwendet. Mein Compiler, GCC-4.7 (32-bit PowerPC, von MacPorts), hat diesen Parameter akzeptiert, obwohl es technisch "void ..." (ein unvollständiger Typ) ist und der Artikel sagte, dass 'void' nicht verwendet werden konnte! Ich plante, 'std :: nullptr_t' oder' int' zu verwenden, da ich dem Bibliothekscode keinen wegwerfbaren Typ hinzufügen wollte (aber egal). – CTMacUser

Verwandte Themen