Tou*_*dou 4 c++ variadic-functions function-templates template-meta-programming variadic-templates
我有多个类(Foo和Bar这里简单)
struct Bar {};
struct Foo {};
Run Code Online (Sandbox Code Playgroud)
和一个采用单个模板参数并根据该类型执行某些操作的函数:
template <typename T>
constexpr void doSomething() { cout << "Am I a Foo? " << is_same<T,Foo>::value << endl; }
Run Code Online (Sandbox Code Playgroud)
在我的代码中,为我提供了Foos和Bars 的模板参数包,并且应该doSomething()在它们的每个参数上调用函数(我不在乎函数的执行顺序)。
doStuff<Foo, Bar, Bar>(); // --> True / False / False
Run Code Online (Sandbox Code Playgroud)
到目前为止,我唯一能想到的解决方案是:
template <typename... Ts>
class Doer;
template <>
struct Doer <> {
static constexpr void doStuff() {}
};
template <typename Head, typename... Tail>
struct Doer <Head, Tail...> {
static constexpr void doStuff() {
doSomething<Head>();
Doer<Tail...>::doStuff();
}
};
template <typename... Ts>
constexpr void doStuff() {
return Doer<Ts...>::doStuff();
}
doStuff<Foo, Bar, Bar>(); // --> True / False / False
Run Code Online (Sandbox Code Playgroud)
它可以工作,但是我觉得它很混乱。我必须使用具有部分专业化的类模板,因为功能模板仅支持完全专业化。我也试过
constexpr void doStuff() { }
template <typename Head, typename... Tail>
constexpr void doStuff() {
doSomething<Head>();
doStuff<Tail...>(); // --> Compile Error
}
Run Code Online (Sandbox Code Playgroud)
但是编译器失败了,因为它无法找出doStuff<>()实际情况doStuff()。如果我在可变参数函数中有参数,那么编译器很聪明,可以解决此冲突,因为它应用了模板类型推导:
constexpr void doStuff() { }
template <typename Head, typename... Tail>
constexpr void doStuff(Head arg, Tail... args) {
doSomething<Head>();
doStuff(args...);
}
Foo f1;
Bar b1, b2;
doStuff<Foo, Bar, Bar>(f1, b1, b2); // --> True / False / False
Run Code Online (Sandbox Code Playgroud)
我想念什么吗?有没有一种方法可以使我的可变参数函数不使用函数参数或类模板而工作?
但是编译器失败,因为它无法确定doStuff <>()实际上是doStuff()。
关于什么
template <int = 0>
constexpr void doStuff() { }
template <typename Head, typename... Tail>
constexpr void doStuff() {
doSomething<Head>();
doStuff<Tail...>();
}
Run Code Online (Sandbox Code Playgroud)
?
我的意思是:如果问题是最后,模板可变参数列表为空,则使用默认值的模板参数(完全不同:整数而不是类型)将模板转换为基本形式。
因此,当Tail...为空时,调用doStuff<Tail...>(),即doStuff<>()匹配doStuff<0>()(考虑第一个函数的默认值),因此调用基本情况。
无论如何:如果可以使用C ++ 17,则可以避免递归,并且使用逗号运算符和模板折叠功能可以简单地编写
template <typename... Ts>
constexpr void doStuff() {
(doSomething<Ts>(), ...);
}
Run Code Online (Sandbox Code Playgroud)
在C ++ 14中,您可以模拟模板折叠,如下所示
template <typename... Ts>
constexpr void doStuff() {
using unused = int[];
(void) unused { 0, ((void)doSomething<Ts>(), 0)... };
}
Run Code Online (Sandbox Code Playgroud)
前面的解决方案也可以在C ++ 11上使用,但不能constexpr(但doSomething()不能constexpr在C ++ 11中使用)。
考虑到您不关心函数的执行顺序,我提出了一个可维护的C ++ 11解决方案,constexpr它基于假函数调用中的模板包扩展(或者可能不是假的...再见)。
但这要求doSomething()是constexpr(因此,在C ++ 11中不能是void)并且也doStuff()不能是void
#include <iostream>
template <typename T>
constexpr std::size_t doSomething ()
{ return sizeof(T); }
template <typename ... Ts>
constexpr int fakeFunc (Ts const & ...)
{ return 0; }
template <typename ... Ts>
constexpr int doStuff ()
{ return fakeFunc( doSomething<Ts>()... ); }
int main()
{
constexpr int a { doStuff<char, short, int, long, long long>() };
(void)a;
}
Run Code Online (Sandbox Code Playgroud)