2013-08-16 17 views
11

Matthieu'nun yanıtını here okuduktan sonra, bunu kendim denemeye karar verdim.SFINAE comma operatörünün hükmünü atla

SFINAE, T::foo erişimini gerçekleştirmeye çalışan has_foo işlevini itip kastetmediğinden, benim denemem başarısız oldu.

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

Bir şeyi özlüyor muyum, yoksa bu şekilde yapamaya çalıştığım şey bu mu?

Tam Exemplar aşağıda (I gcc-4.7.2 kullanıyorum):

#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

'Val' adam diyor, foo değil! – Rapptz

+0

@Rapptz - tam olarak! ** "has_foo (true)" yöntemini kullanmanız ve –

+0

Ah erkeği, geri dönüş yöntemini kullanması gerekiyor. Ben sadece hızlı bir şekilde gözden geçirdim, bu yüzden soruyu tam olarak anlamadım :) – Rapptz

cevap

10

burada birincil mesele AFAICS bir constexpr olarak çalışma zamanı referans kullanarak olmasıdır işlev parametresi. Bunu değiştirmek sadece iyi çalışıyor.

#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

Eğer yapabilirsem izleyin. Tümünü yakala arasında ayrım yapmak için bir “int” parametresi (ve bu türün değerini geçerek) kullanıyorsunuz ve bunu yakalıyorsunuz. Diyelim ki 'foo' veya 'bar' üye değerini (bu sırayla) alacağım şartım var. Bu yöntem bu işe uygun mu, yoksa başka bir yol alınmalı mı? –

+0

@RedXIII: Evet, bu yöntem uygundur. Tümünü yakalama durumunda, sadece '.bar' kullanın. Bir ".foo" varsa, ilk yöntem kullanılır. Eğer '.foo' yoksa ama '.bar 'varsa, yakalama seçeneği seçilir ve derlenir. Eğer ikisi de yoksa, yakalananlar seçilir ve derleme yapılmaz. – MSalters

+1

@RedXIII: Evet, “int” ve “long”, (int '-' 'long' ->' ... ') komutlarını kullanarak bunu birden fazlaya genişletebilirsiniz, ancak bunu da özelleştirebilirsiniz. Temel sınıfları kullanarak keyfi bir tercihe. – Puppy