可变参数模板的两个实例

简介

模板是C++的一块重要内容,在模板中比较复杂的就是可变参数模板。这篇文章主要通过三个实例来进行可变参数模板的学习,包括类模板和函数模板,其中涉及到模板递归和模板的特化与偏特化。

类模板

下面是一个打印tuple元素的例子,例如我们创建了一个tuple为make_tuple(5, “hello”, bitset<16>(80)),那么打印输出为{5,hello,0000000001010000}。当然写程序的方法有很多,下面的代码只是为了学习可变参数的类模板实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
template <int id, int Max, typename... Args>
class print_tuple //类模板
{
public:
static void print(ostream &os, const tuple<Args...> &t)
{
os << get<id>(t) << (id + 1 == Max ? "" : ",");
print_tuple<id + 1, sizeof...(Args), Args...>::print(os, t);//递归调用函数的同时,也会创建类(实例化),
//这个过程中id会不断加1,直到前两个类型参数相同时会调用偏特化模板
}
};
template <int Max, typename... Args>
class print_tuple<Max, Max, Args...> // 偏特化模板
{
public:
static void print(ostream &os, const tuple<Args...> &t)
{
}
};
template<typename... Args>
ostream &operator<<(ostream &os, const tuple<Args...> &t)
{
os << "{";
print_tuple<0, sizeof...(Args), Args...>::print(os, t);
return os << "}";
}

上面这个例子是在递归实例化类模板。下面是一个递归继承的类模板,和上面的实例相比要更复杂一点,我们在这个例子中表述的继承关系如图所示:
variadic_template
实例代码如下,设计巧妙的地方在于构造函数存在递归调用关系,而不是创建临时对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <typename... Values> class my_tuple; //通用版本
template <> class my_tuple<> {}; // 特例化版本
template <typename Head, typename... Tail>
class my_tuple<Head, Tail...> : private my_tuple<Tail...>
{
typedef my_tuple<Tail...> inherited;
public:
my_tuple() {}
my_tuple(Head v, Tail... vtail) : m_head(v), inherited(vtail...) {} //调用类模板的构造函数
Head head() { return m_head; }
inherited& tail() { return *this; } //子类对象转化成父类引用
protected:
Head m_head;
};

函数模板

函数模板没有偏特化一说,主要是全特化和普通的函数模板。下面的实例实现了一个简单的功能就是print(12, 30.90, “he”),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
template <class T>
void print(const T &t) // 这是递归调用结束的条件
{
cout << t << endl;
}
template <typename T, typename... Args>
void print(const T &t, const Args&... args)
{
cout << t << " ";
print(args...);
}

应该说函数模板和类模板相比起来要容易很多,但是需要注意递归结束的条件,把握这一点应该就没有太大的问题。

声明

若有错误,欢迎讨论。严禁抄袭,仅用于学习。

坚持原创技术分享,您的money将鼓励我继续创作!