没有形式参数的可变参数函数模板

时间:2013-05-26 11:04:31

标签: c++ templates c++11 variadic-templates

这就是我要做的事情:

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

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

3 个答案:

答案 0 :(得分:11)

另一种方法是将非模板函数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 :(得分: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();
}

答案 2 :(得分:0)

从c ++ 20开始,您可以对函数使用约束,而不是SFINAE。

template <typename... Ts>
requires (sizeof...(Ts) == 0)
void f(){}

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