Variadic Templates
可变参数模板,函数的参数可以有任意多个,参数的类型任意
1. 实现函数递归调用
实现打印任意多个不同类型参数
#include <iostream>
using namespace std;
//无参数版本
void print() {}
//n+1 个参数版本
template <class T, class... Types>
void print(const T& firstArg, const Types&... args) {
cout << firstArg << endl;
print(args...); //n个参数版本
}
int main() {
print(100, 4.533, "hello world!");
return 0;
}
输出结果为
100
4.533
hello world!
在VS中单步跟踪,我发现编译器将此函数特化为三个版本
分别为:
1. void print(int, double, char *)
2. void print(dobule, char *)
3. void print(char *)
我们自己实现的无参版本 void print()
在函数调用过程中,此函数实现了递归调用,1调用2, 2调用3,3调用我们自己实现的无参版本
然后再返回3,2,1
实现任意多个参数的max
#include <iostream>
#include <algorithm>
using namespace std;
//特化,分解到只有一个参数时返回n
template<class T>
T maximum(T n) {
return n;
}
template<class T, class... Args>
T maximum(T n, Args... args) {
return std::max(n, maximum(args...));
}
int main() {
cout << maximum(1,4,8,500,67,25,7,7,5,634) << endl; //634
cout << maximum(1.6,78.7,346.64) << endl; //346.64
return 0;
}
2. 实现类的递归继承
#include <iostream>
#include <string>
//声明
template<class... Values> class tuple;
//无参版本,空类
template<> class tuple<> {};
template<class Head, class... Tail>
class tuple<Head, Tail...> : private tuple<Tail...> {
typedef tuple<Tail...> inherited;
public:
tuple() {}
//调用父类构造函数
tuple(Head x, Tail... vtail) :_head(x), inherited(vtail...) {}
Head head() {
return _head;
}
inherited& tail() {
return *this;
}
private:
Head _head;
};
int main() {
tuple<int, double, std::string> t(1, 35.7, "Hello World!");
std::cout << t.head() << std::endl; //输出1
std::cout << t.tail().head() << std::endl; //35.7
std::cout << t.tail().tail().head() << std::endl; //Hello World!
}
一个参数的tuple继承空类tuple,两个参数的继承一个参数的,三个参数的继承两个参数的……
3. 实现类的递归合成
#include <iostream>
#include <string>
//声明
template<class... Values> class tuple;
//无参版本,空类
template<> class tuple<> {};
template<class Head, class... Tail>
class tuple<Head, Tail...> : private tuple<Tail...> {
typedef tuple<Tail...> composed;
public:
tuple() {}
//调用父类构造函数
tuple(Head x, Tail... vtail) :_head(x), _tail(vtail...) {}
Head head() {
return _head;
}
composed& tail() {
return _tail;
}
private:
Head _head;
composed _tail;
};
int main() {
tuple<int, double, std::string> t(1, 35.7, "Hello World!");
std::cout << t.head() << std::endl; //输出1
std::cout << t.tail().head() << std::endl; //35.7
std::cout << t.tail().tail().head() << std::endl; //Hello World!
}
一个参数的tuple含有空类,两个参数的tuple含有一个参数的tupe,三个参数的含有两个参数的……
下面我们借助类模板实现一个打印tuple的函数
对于tuple<int, double, std::string> t(1, 35.7, "Hello World!")
我们希望输出的格式为[1,35.7,Hello World!]
这里采用标准库的tuple 因为里面实现了函数get
#include <iostream>
#include <tuple>
#include <string>
using namespace std;
template <int INDEX, int MAX, class... Args>
struct print_tuple {
static void print(ostream& os, const tuple<Args...>& t) {
os << get<INDEX>(t) << (INDEX + 1 == MAX ? "" : ",");
print_tuple<INDEX + 1, MAX, Args...>::print(os, t);
}
};
//偏特化,当INDEX 等于 MAX时什么都不做
template <int MAX, class... Args>
struct print_tuple<MAX, MAX, Args...> {
static void print(ostream& os, const tuple<Args...>& t) {}
};
//重载操作符 <<
template <class... Args>
ostream& operator<< (ostream& os, const tuple<Args...>& t) {
os << "[";
print_tuple<0, sizeof...(Args), Args...>::print(os, t);
os << "]";
return os;
}
int main(){
tuple<int, double, string> t(100, 23.23, "Hello World");
cout << t << endl;
}
输出[100,23.23,Hello World]
我最初是想借助函数模板用这样的方式输出的
template <class... Args>
ostream& operator<< (ostream& os, const tuple<Args...>& t) {
os << "[";
int size = sizeof...(Args);
for (int i = 0; i < size; ++i)
os << get<i>(t) << (i == size - 1 ? "" : ",");
os << "]";
return os;
}
但是编译肯定不能通过,爆出成吨的errors,因为对于函数get<T>(t)
,模板参数T
必须是一个常量,要在编译期就能确定
4. 小结
variadic templates为c++11的重量级特性,能解决很多问题,在库的实现中有着广泛的应用,比如模板元编程黑魔法-.-,掌握了大概,离高手级的应用还差很远很远