2013-05-26 22 views
5

To właśnie staram się robić:o zmiennej liczbie argumentów funkcja szablon bez parametrów formalnych

// base case 
void f() {} 

template <typename T, typename... Ts> 
void f() { 
    // do something with T 
    f<Ts...>(); 
} 

int main() { 
    f<int, float, char>(); 
    return 0; 
} 

To nie kompiluje:

prog.cpp: In instantiation of ‘void f() [with T = char; Ts = {}]’: 
prog.cpp:6:5: recursively required from ‘void f() [with T = float; Ts = {char}]’ 
prog.cpp:6:5: required from ‘void f() [with T = int; Ts = {float, char}]’ 
prog.cpp:10:25: required from here 
prog.cpp:6:5: error: no matching function for call to ‘f()’ 
prog.cpp:6:5: note: candidate is: 
prog.cpp:4:6: note: template<class T, class ... Ts> void f() 
prog.cpp:4:6: note: template argument deduction/substitution failed: 
prog.cpp:6:5: note: couldn't deduce template parameter ‘T’ 

This wątek pokazuje sposób, aby rozwiązać ten problem, ale przypadek podstawowy musi być szablonem. Nie podoba mi się to, ponieważ o ile rozumiem, będę musiał zduplikować kod, który działa z T. Czy istnieje sposób, aby tego uniknąć?

Dotychczas wymyśliłem dwóch rozwiązań (http://ideone.com/nPqU0l):

template <typename...> struct types_helper {}; 

// base case 
void f(types_helper<>) {} 

template <typename T, typename... Ts> 
void f(types_helper<T, Ts...>) { 
    // do something with T 
    f(types_helper<Ts...>()); 
} 

int main() { 
    f(types_helper<int, float, char>()); 
    return 0; 
} 

http://ideone.com/yyg6y9:

#include <type_traits> 

struct end_of_list; 

template <typename T> 
void f() { 
    static_assert(std::is_same<T, end_of_list>::value, "error"); 
} 

template <typename T1, typename T2, typename... Ts> 
void f() { 
    // do something with T 
    f<T2, Ts...>(); 
} 

int main() { 
    f<int, float, char, end_of_list>(); 
    return 0; 
} 

Zastanawiam się, czy istnieje lepszy sposób to zrobić.

Odpowiedz

9

Innym sposobem jest toczenie funkcji non-template f do funkcja szablonu variadic, która akceptuje zero lub więcej argumentów szablonu (druga f wymaga jednego lub więcej argumentów szablonu). Aby uniknąć niejednoznaczności, SFINAE odrzuca tę funkcję szablonu, gdy liczba argumentów nie wynosi zero. Cóż, kod jest lepszy niż 1000 słów:

#include <type_traits> 

template <typename... Ts> 
typename std::enable_if<sizeof...(Ts) == 0>::type f() { 
} 

template <typename T, typename... Ts> 
void f() { 
    // do something with T 
    f<Ts...>(); 
} 
+1

To miło. Chociaż 'enable_if' wydaje się być wadliwy w visual studio, więc musiałem napisać to jak' decltype (typname std :: enable_if :: type()) '. – catscradle

+0

krótka i łatwa korekta do początkowego kodu, podoba mi się to. –

2

Ponieważ szablony klasy mogą być częściowo wyspecjalizowane, inną możliwością jest użycie szablonów klas do pracy i mieć swój delegata funkcyjny do nich:

template<typename... Ts> 
struct caller 
{ 
    static void call() { } // Base case, terminates recursion 
}; 

template<typename T, typename... Ts> 
struct caller<T, Ts...> 
{ 
    static void call() 
    { 
     // Do something with T 
     caller<Ts...>::call(); 
    } 
}; 

template<typename... Ts> 
void f() { 
    caller<Ts...>::call(); 
} 
+0

O, widzę. Próbowałem coś takiego jak 'template <> struct caller <>' first, które nie działało. Dzięki! – catscradle

+0

@catscradle: Cieszę się, że pomogło :) –

Powiązane problemy