2013-08-16 10 views
11

Nachdem ich Matthieus Antwort here gelesen hatte, beschloss ich, das selbst zu versuchen.SFINAE declype comma operator trick

Mein Versuch kann nicht kompiliert werden, da SFINAE die has_foo-Funktion, die versucht, auf T::foo zuzugreifen, nicht eingibt und ausgibt.

error: ‘struct Bar’ has no member named ‘foo’ 

Fehle ich etwas, oder versuche ich auf diese Weise nicht zu tun?

(Ich bin mit gcc-4.7.2)

Voll Examplar unter:

#include <iostream> 

// culled by SFINAE if foo does not exist 
template<typename T> 
constexpr auto has_foo(T& t) -> decltype((void)t.foo, bool()) 
{ 
    return true; 
} 
// catch-all fallback for items with no foo 
constexpr bool has_foo(...) 
{ 
    return false; 
} 
//----------------------------------------------------- 

template<typename T, bool> 
struct GetFoo 
{ 
    static int value(T& t) 
    { 
     return t.foo; 
    } 
}; 
template<typename T> 
struct GetFoo<T, false> 
{ 
    static int value(T&) 
    { 
     return 0; 
    } 
}; 
//----------------------------------------------------- 

template<typename T> 
int get_foo(T& t) 
{ 
    return GetFoo<T, has_foo(t)>::value(t); 
} 
//----------------------------------------------------- 

struct Bar 
{ 
    int val; 
}; 

int main() 
{ 
    Bar b { 5 }; 
    std::cout << get_foo(b) << std::endl; 
    return 0; 
} 
+0

Es sagt 'Val' Mann, nicht' foo'! – Rapptz

+0

@Rapptz - genau! Es soll ** die Methode 'has_foo (true)' ** cull 'und die Fallback-Methode verwenden –

+0

Ah Mann, Entschuldigung. Ich habe es einfach schnell überflogen, so dass ich die Frage nicht ganz verstanden habe :) – Rapptz

Antwort

10

Die primäre Ausgabe hier AFAICS ist, dass Sie die Laufzeit Referenz als constexpr verwenden Funktionsparameter. Das Ersetzen funktioniert gut.

#include <iostream> 

// culled by SFINAE if foo does not exist 
template<typename T> 
constexpr auto has_foo(int) -> decltype(std::declval<T>().foo, bool()) 
{ 
    return true; 
} 
// catch-all fallback for items with no foo 
template<typename T> constexpr bool has_foo(...) 
{ 
    return false; 
} 
//----------------------------------------------------- 

template<typename T, bool> 
struct GetFoo 
{ 
    static int value(T& t) 
    { 
     return t.foo; 
    } 
}; 
template<typename T> 
struct GetFoo<T, false> 
{ 
    static int value(T&) 
    { 
     return 0; 
    } 
}; 
//----------------------------------------------------- 

template<typename T> 
int get_foo(T& t) 
{ 
    return GetFoo<T, has_foo<T>(0)>::value(t); 
} 
//----------------------------------------------------- 

struct Bar 
{ 
    int val; 
}; 
struct Foo { 
    int foo; 
}; 

int main() 
{ 
    Bar b { 5 }; 
    Foo f { 5 }; 
    std::cout << get_foo(b) << std::endl; 
    std::cout << get_foo(f) << std::endl; 
    return 0; 
} 
+0

Follow-up, wenn ich darf. Sie verwenden einen 'int' -Parameter (und übergeben einen Wert dieses Typs), um zwischen catch all zu unterscheiden und dies zu erfassen. Nehmen wir an, ich habe eine Anforderung, dass ich "foo" oder "bar" Mitgliedswert (in dieser Reihenfolge) nehmen soll. Ist diese Methode für diese Aufgabe geeignet oder sollte eine andere Route gewählt werden? –

+0

@RedXIII: Ja, diese Methode ist geeignet. Verwenden Sie im Catch-All-Fall einfach '.bar'. Wenn es ein '.foo' gibt, wird die erste Methode verwendet. Wenn es keine '.foo' gibt, aber eine' .bar', wird der Catch-All ausgewählt und kompiliert. Wenn es keine gibt, wird der Catch-All ausgewählt und es wird nicht kompiliert. – MSalters

+1

@RedXIII: Ja, Sie können es auf mehr als eins erweitern, indem Sie say, 'int' und' long', (Reihenfolge 'int' ->' long' -> '...') verwenden, aber Sie können dies auch anpassen zu einer beliebigen Präferenz durch Verwendung von Basisklassen. – Puppy