2012-01-05 13 views
7

To tylko pytanie o styl: nie podoba mi się sposób C++ do metaprogramowania szablonów, który wymaga użycia typu zwrotnego lub dodania dodatkowego atrapa dla trików z SFINAE . Tak, pomysł wpadłem jest umieścić coś SFINAE w samej definicji argumenty szablonu, tak:boost :: enable_if nie w sygnaturze funkcji

#include <iostream> 
#include <boost/type_traits/is_array.hpp> 
#include <boost/utility/enable_if.hpp> 
using namespace std; 

template <typename T, typename B=typename boost::enable_if< boost::is_array<T> >::type > void asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T, typename B=typename boost::disable_if< boost::is_array<T> >::type > void asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

int main() { 
    asd<int>(); 
    asd<int[]>(); 
} 

Ten przykład zrobić g ++ skarżyć:

../src/afg.cpp:10:97: error: redefinition of ‘template void asd()’

SFINAE tam sobie działa, ponieważ jeśli usunę na przykład jeden z disable_if, błąd kompilator:

../src/afg.cpp:15:12: error: no matching function for call to ‘asd()’

co jest, co chcę.

Czy istnieje sposób na zrealizowanie SFINAE nie w "normalnym" sygnaturze funkcji, to jest typ zwracany + lista argumentów?

EDIT: To jest w końcu to, co mam zamiar spróbować w rzeczywistym kodu:

#include <iostream> 
#include <type_traits> 
using namespace std; 

template <typename T, typename enable_if< is_array<T>::value, int >::type =0 > void asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T, typename enable_if< !is_array<T>::value, int >::type =0 > void asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

int main() { 
    asd<int[]>(); 
    asd<int>(); 
} 

używam C++ 0x rzeczy zamiast doładowania bo jak długo muszę C++ 0x dla użycia domyślnych argumentów szablonu, nie widzę powodu, aby używać boost, który jest jego prekursorem.

Odpowiedz

6

argumenty Domyślny szablon nie są częścią podpisu szablonów funkcyjnych. Ale typem parametrów szablonu jest.Więc można wykonać następujące czynności i móc go przepełniać

template < 
    typename T, 
    typename boost::enable_if< 
    boost::is_array<T>, int 
    >::type = 0 
> 
void asd() { 
    cout<<"This is for arrays"<<endl; 
} 

template < 
    typename T, 
    typename boost::disable_if< 
    boost::is_array<T>, int 
    >::type = 0 
> 
void asd() { 
    cout<<"This is for arrays"<<endl; 
} 
+0

wow, nie wiedziałem, że nawet typ szablonu może być dynamiczny! Nie akceptuję twojej odpowiedzi ponieważ najpierw muszę sprawdzić, czy ta sztuczka może mieć zastosowanie do mojego kodu, ale jestem pewien, że tak. –

8

Ponieważ C++ 11 umożliwił, używam tylko enable_if (lub odwrotnie disable_if) wewnątrz argumentów szablonu, tak jak to robisz. Jeśli/kiedy występuje kilka przeciążeń, używam fałszywych, domyślnych argumentów szablonu, które powodują, że listy parametrów szablonów różnią się. Tak aby ponownie użyć przykład, że byłoby:

template< 
    typename T 
    , typename B = typename boost::enable_if< 
     boost::is_array<T> 
    >::type 
> 
void asd() { 
    cout << "This is for arrays" << endl; 
} 

template< 
    typename T 
    , typename B = typename boost::disable_if< 
     boost::is_array<T> 
    >::type 
    , typename = void 
> 
void asd() { 
    cout << "This is for arrays" << endl; 
} 

Inną alternatywą nie brudząc typ zwrotny (który nie jest dostępny w niektórych przypadkach, np operatorów konwersji), która istniała od C++ 03 jest użycie domyślnych argumentów :

template<typename T> 
void 
foo(T t, typename std::enable_if<some_trait<T>::value>::type* = nullptr); 

nie używam tej formy jak lubię „bawić” z typami argumentów, tak samo jak z typem powrotu, i dla zachowania spójności (ponieważ nie jest to wykonalne we wszystkich przypadkach).

2

To może nie być dokładnie to, o co prosisz, ale co z dobrą starą specjalizacją szablonów?

template<typename T> 
struct asd 
{ 
    static void fgh() 
    { 
     std::cout << "not an array\n"; 
    } 
}; 

template<typename T> 
struct asd<T[]> 
{ 
    static void fgh() 
    { 
     std::cout << "an array of unknown size\n"; 
    } 
}; 

template<typename T, size_t N> 
struct asd<T[N]> 
{ 
    static void fgh() 
    { 
     std::cout << "an array of known size\n"; 
    } 
}; 

int main() 
{ 
    asd<int>::fgh(); 
    asd<int[]>::fgh(); 
    asd<int[42]>::fgh(); 
} 
+0

Dobrze, ale jest bardziej długa niż w przypadku innej odpowiedzi. –

+3

Przynajmniej drukuje różne rzeczy dla tablic i nie-tablic ;-) – fredoverflow

2

So, is there a way to accomplish SFINAE not in the "normal" signature of a function, that is return type + argument list?

Cóż, istnieje sposób, aby uzyskać ten sam efekt bez użycia SFINAE wcale — przeciążeniem:

#include <iostream> 
#include <type_traits> 

void asd_impl(std::true_type&&) 
{ 
    std::cout << "This is for arrays\n"; 
} 

void asd_impl(std::false_type&&) 
{ 
    std::cout << "This is not for arrays\n"; 
} 

template<typename T> 
void asd() 
{ 
    asd_impl(std::is_array<T>()); 
} 

int main() 
{ 
    asd<int>(); 
    asd<int[]>(); 
} 

Ten styl jest o wiele bardziej czytelny IMO, i jest szeroko stosowane w template- ciężkie biblioteki, takie jak Boost. Spirit, ponieważ ma tendencję do kompilacji szybciej i działa lepiej z kompilatorami posiadającymi mniej niż gwiezdny szablon/wsparcie SFINAE (np. VC++ i Sun Studio).

Online demo.

+0

+1, dla prostego pytania prawda/fałsz, nie trzeba zatrudniać SFINAE. Będzie tylko owłosiona na więcej niż jedną cechę, ponieważ nie można po prostu połączyć ich z operatorami logicznymi. (Co mnie zastanawia, czy można przeciążyć 'operator ||' i 'operator &&' dla 'true_type' i' false_type' ... hm.) – Xeo

+0

@Xeo: 'std :: integral_constant 'powinien mieć właściwą semantykę. – ildjarn

+0

Tak, ale to wygląda trochę chaotycznie, dlatego zastanawiałem się nad przeciążeniem operatora. :) W zasadzie to po prostu wyglądałoby ładniej. – Xeo

8

dobrze, zazwyczaj używają tych makr, aby enable_if konstruuje wiele czystsze (nawet pracować w większości kompilatorów C++ 03)

#define ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE(...) __VA_ARGS__>::type 
#define FUNCTION_REQUIRES(...) typename boost::enable_if<boost::mpl::and_<__VA_ARGS__, boost::mpl::bool_<true> >, ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE 
#define EXCLUDE(...) typename boost::mpl::not_<typename boost::mpl::or_<__VA_ARGS__, boost::mpl::bool_<false> >::type >::type 

Wtedy można zdefiniować swoją funkcję tak:

template <typename T > 
FUNCTION_REQUIRES(is_array<T>) 
(void) asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T > 
FUNCTION_REQUIRES(EXCLUDE(is_array<T>)) 
(void) asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

Jedyną rzeczą jest, trzeba umieścić nawiasów wokół zwracanego typu. Jeśli je zapomnisz, kompilator powie coś takiego, że "ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE" jest niezdefiniowane.

Powiązane problemy