2013-08-16 4 views
11

Прочитав ответ Матти here, я решил попробовать это сам.SFINAE decltype comma operator trick

Моя попытка не скомпилирована, потому что SFINAE не использует и не снимает функцию has_foo, которая пытается получить доступ к T::foo.

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

Я что-то упускаю, или это то, что я пытаюсь сделать невозможным таким образом?

(я использую GCC-4.7.2)

Полный examplar ниже:

#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' человек, а не' foo'! – Rapptz

+0

@ Rapptz - точно! Предполагается, что ** отбросить ** метод 'has_foo (true)' и использовать метод возврата –

+0

Ах, извините. Я просто быстро просмотрел его, поэтому я не полностью понял вопрос :) – Rapptz

ответ

10

Основной проблемой здесь является то, что AFAICS вы используете время выполнения ссылку как constexpr параметр функции. Замена этого работает просто отлично.

#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

Последующие действия, если можно. Вы используете параметр 'int' (и передаете значение этого типа), чтобы различать между catch all и catch this. Допустим, у меня есть требование, чтобы я принял значение члена 'foo' или' bar' (в этом порядке). Является ли этот метод подходящим для этой задачи или должен быть принят другой маршрут? –

+0

@RedXIII: Да, этот метод подходит. В общем случае просто используйте '.bar'. Если есть '.foo', используется первый метод. Если нет '.foo', но есть' .bar', выбирается catch-all и компилируется. Если их нет, выбирается catch-all, и он не компилируется. – MSalters

+1

@RedXIII: Да, вы можете расширить его до нескольких, используя, например, 'int' и' long', (порядок 'int' ->' long' -> '...'), но вы также можете настроить это к произвольному предпочтению с использованием базовых классов. – Puppy

Смежные вопросы