2013-05-26 140 views
5

这就是我想要做的事:可变参数函数模板

// 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; 
} 

这并不编译:

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线显示了一种方法来解决这个问题,但基本情况必须是模板。我不太喜欢它,因为据我所知,我将不得不复制与T一起工作的代码。有没有办法避免这种情况?

到目前为止,我想出了两种解决方案(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; 
} 

我不知道是否有更好的方法来做到这一点。

回答

9

另一种方式是转向非模板函数f成可接受零个或多个模板参数的可变模板函数(其他f需要一个或多个模板参数)。然后为了避免模棱两可,当参数个数不为零时,SFINAE将这个模板函数移开。那么,一个代码优于1000字:

#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

这很好。尽管'enable_if'似乎在Visual Studio中被窃听,所以我不得不像'decltype(typename std :: enable_if :: type())'一样编写它。 – catscradle

+0

对初始代码的简短修复,我喜欢这样。 –

2

由于类模板可以部分专用,另一种可能性是使用类模板做的工作,和你的功能委托给他们:

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

哦,我明白了。我首先尝试了像'template <> struct caller <>'这样的东西,但是没有成功。谢谢! – catscradle

+0

@catscradle:很高兴帮助:) –