2013-08-16 13 views
11

Po przeczytaniu odpowiedzi Matthieu here, postanowiłem spróbować tego sam.SFINAE trick operatora przecinka deklinowanego

Moja próba nie doszła do skutku, ponieważ SFINAE nie uruchamia i nie odbiera funkcji has_foo, która próbuje uzyskać dostęp do T::foo.

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

Czy brakuje mi czegoś, czy też tego właśnie próbuję nie robić w ten sposób?

(Używam gcc-4.7.2)

Pełna examplar poniżej:

#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

Mówi "val' człowiek, a nie' foo'! – Rapptz

+0

@Rapptz - dokładnie! Powinno to ** anulować ** metodę 'has_foo (true)' i zastosować metodę awaryjną. –

+0

Ah man, sorry. Po prostu szybko przejrzałem przez to, więc nie w pełni zrozumiałem pytanie :) – Rapptz

Odpowiedz

10

Podstawowym problemem jest to, że tutaj AFAICS używasz run-time odniesienie jako constexpr parametr funkcji. Zastąpienie tego działa dobrze.

#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

Kontynuuj, jeśli mogę. Używasz parametru 'int' (i przekazujesz wartość tego typu), aby odróżnić catch all i przechwycić to. Powiedzmy, że mam wymóg, że wezmę wartość 'foo' lub' bar' member (w tej kolejności). Czy ta metoda jest odpowiednia do tego zadania, czy inna droga powinna zostać podjęta? –

+0

@RedXIII: Tak, ta metoda jest odpowiednia. W przypadku catch-all, po prostu użyj '.bar'. Jeśli istnieje '.foo', używana jest pierwsza metoda. Jeśli nie ma '.foo', ale istnieje' .bar', to catch-all jest wybierany i kompilowany. Jeśli nie ma żadnego, catch-all jest wybrany i nie kompiluje się. – MSalters

+1

@RedXIII: Tak, możesz rozszerzyć go na więcej niż jeden, używając poleceń: 'int' i' long', (order 'int' ->' long' -> '...'), ale możesz też dostosować to do arbitralnej preferencji za pomocą klas bazowych. – Puppy