2015-03-13 13 views
5

Nie mogę zrozumieć drugiego scenariusza przedstawionego here. Mówi:enable_if to Dodaj parametr funkcji, który ma domyślny argument?

• Scenariusz 2: Dodanie parametru funkcyjnego, który ma domyślną argumentu:

template <your_stuff> your_return_type_if_present 
yourfunction(args, enable_if_t<your condition, FOO> = BAR) { 
    // ... 
} 

Scenariusz 2 pozostawia parametr nienazwany. Można powiedzieć: ::type Dummy = BAR, ale nazwa Dummy jest bez znaczenia, a podanie nazwy prawdopodobnie wyzwoli nieprzypisane ostrzeżenie o wartości parametru. Musisz wybrać domyślny argument parametru funkcji FOO i BAR. Można powiedzieć: int i 0, ale użytkownicy tego kodu mogą przypadkowo przekazać do funkcji dodatkową liczbę całkowitą, która zostanie zignorowana. Zamiast tego zaleca się stosowanie void ** i albo 0 lub nullptr bo prawie nic nie jest wymienialny na void **:

template <your_stuff> your_return_type_if_present 
yourfunction(args, typename enable_if<your_condition, void **>::type=nullptr) { 
// ... 
} 

Jeśli scenariusz 2 pozostawia parametr nienazwany następnie w co może być wykorzystane? Czy istnieje sposób, aby kod taki jak ten zadziałał z enable_if?

enum otype {oadd,omull}; 
template<otype o> 
int add(int num1, std::enable_if<o == oadd, int>::type int= num2) 
{ 
    if (o == omull) return num1 * num1; 
    if (o == oadd) return num1 + num2; 
} 
+0

Może to być przydatne, gdy (ab) w rozdzielczości przeciążenia na przykład. – BartoszKP

+0

Nie można osiągnąć części "omull". – Jarod42

+0

enable_if włącza funkcję, a nie parametr. –

Odpowiedz

3

enable_if przykładów (jeżeli pomaga):

Dla funkcji z nie typ void:

do pojedynczego stanu:

template <template T, typename std::enable_if<!std::is_same<T,std::string>::value>::type* = nullptr > 
T func(T x){} 

do wielokrotnego warunek:

template <template T, typename std::enable_if<!std::is_same<T,std::string>::value &&!std::is_same<T,int>::value>::type* = nullptr > 
T func(T x){} 


Dla funkcji z void Zwraca typ:

do pojedynczego warunku:

template <template T> 
typename std::enable_if<!std::is_same<T,std::string>::value>::type 
func(T x){} 

Na stanie Wielokrotność:

template <template T> 
typename std::enable_if<!std::is_same<T,std::string>::value &&!std::is_same<T,int>::value>::type 
func(T x){} 

Nie zapomnij podać #include <type_traits>

6

dokumentacji Microsoftu there nie jest tak jasne jak słońce. Zamiast tego użyj this.

Zapewnienie szablon funkcji z nienazwanej domyślnej parametru w postaci:

typename enable_if<your_condition, void **>::type = nullptr 

(jak sugeruje MS pisarz), jest przydatna w przypadku - i tylko w przypadku - chcesz napisać kilka przeciążeniem szablon funkcji o różnych zachowaniach , które są kontrolowane przez jeden lub więcej argumentów szablonu. Następnie, przez , zastępując your_condition warunkiem spełniającym odpowiednie wymaganie dla argumentu szablonu, można zarejestrować zasadę SFINAE , aby wybrać konkretne przeciążenie, które ma być tworzone dla określonych argumentów szablonu.

Parametr SFINAE - nazwijmy to - jest nieużywany przez instancjonowaną funkcję; istnieje wyłącznie w celu sprowokowania SFINAE w szablonie funkcji . W związku z tym może być bezimienny, a więc musi być domyślnie: nie może wymuszać dostarczenia dodatkowego, bezużytecznego argumentu, gdy użytkownik wywoła szablon funkcji.

Na przykład:

#include <type_traits> 
#include <iostream> 

template <typename T> 
T foo(T && t, 
    typename std::enable_if<std::is_same<T,int>::value, void **>::type = nullptr) 
{ 
    std::cout << "Doubling " << t << " gives " << (t + t) << std::endl; 
    return t + t; 
} 

template <typename T> 
T foo(T && t, 
    typename std::enable_if<!std::is_same<T,int>::value, void **>::type = nullptr) 
{ 
    std::cout << "Squaring " << t << " gives " << (t * t) << std::endl; 
    return t * t; 
} 

using namespace std; 

int main() 
{ 
    cout << foo(2) << endl; 
    cout << foo(3.3) << endl; 
    return 0; 
} 

wyjściowa wynosi:

Doubling 2 gives 4 
4 
Squaring 3.3 gives 10.89 
10.89 

W tych dwóch przeciążeń funkcji szablonu foo, pierwszy podwaja to typ T argumentem a drugi kwadraty jej argument, a parametr SFINAE służy do stwierdzenia, że ​​podwojenie obciążenia zostanie utworzone jako , jeśli T jest int i że przeciążenie kwadraturowe zostanie wybrane inaczej.

Kiedy T jest int warunek:

!std::is_same<T,int>::value 

który kontroluje parametry SFINAE przeciążenia kwadratury jest fałszywe. W związku z tym specyfikator typu:

typename std::enable_if<!std::is_same<T,int>::value, void **>::type = nullptr 

nie można skompilować. To jest niepowodzenie substytucji w rozdzielczości szablonu. W przypadku przeciążenia do kwadratu zastępowanie dla jest nieprzewidywalne: . Przeciążenie do kwadratu jest więc eliminowane z biegu, a jedynie dublowanie jest pozostawione do utworzenia wywołania funkcji w postaci .

Kiedy T jest (powiedzmy) double i nie int, następnie dokładnie odwrotnie dzieje i tylko przeciążenie kwadratury przetrwa rozdzielczość matrycy. Zadzwoń pod foo(2) , a otrzymasz podwojenie. Zadzwoń pod numer foo(3.3), a otrzymasz kwadraturę.

Parametr MS Parametr SFINAE tutaj jest niepotrzebnie długi.

template< bool B, class T = void > 
struct enable_if; 

według C++ 11 standardowych a później domyślne T do void. Tak jak od:

typename std::enable_if<some_condition, void **>::type = nullptr 

może być równie dobrze w skrócie:

typename std::enable_if<some_condition>::type * = nullptr 

a od C++ 14 Standard posiada:

template< bool B, class T = void > 
using enable_if_t = typename enable_if<B,T>::type 

tak samo możliwe, parametr SFINAE być dodatkowo skrócone do:

std::enable_if_t<some_condition> * = nullptr 

Stosowanie SFINAE szablonu funkcji parametr do sprawy, że wskazał w swoim poście , byś napisać jak się z:

enum ops { 
    add, 
    multiply 
}; 

template<ops Op> 
int op(int const & lhs, int const & rhs, 
     std::enable_if_t<Op == add> * = nullptr) 
{ 
    return lhs + rhs; 
} 

template<ops Op> 
int op(int const & lhs, int const & rhs, 
     std::enable_if_t<Op == multiply> * = nullptr) 
{ 
    return lhs * rhs; 
} 

... 

auto i = op<add>(2,3); 
auto j = op<multiply>(2,3); 

... 
// C++14 
+0

Dziękujemy! W końcu znalazłem jasne wyjaśnienie dotyczące używania 'enable_if' dla SFINAE. Twoja odpowiedź pozwoliła mi wiele się nauczyć! Skróty do 'std :: enable_if_t * = nullptr' są naprawdę pomocne! Chcę zapytać, dlaczego skracam 'void **' do 'std :: enable_if :: type *', które, moim zdaniem, jest w rzeczywistości 'void *'? Doceń to w zaawansowanym. – astroboylrx

Powiązane problemy