可变模板参数(c++11新特性)

Variadic Templates

  • Variadic Templates
    谈的是template
    • function template
    • class template
  • 变化的是template parameters
    • 参数个数:利用参数个数注意递减的特性,实现递归函数调用.使用function template完成
    • 参数类型:利用参数个数逐一递减,参数类型也逐一递减的特性,实现递归继承或递归复合,以class template完成.

可变模板参数写的一个小例子

针对函数
模板参数包,函数参数类型包,函数参数包

1
2
3
4
5
6
7
8
9
void print() {}	//非模板,和模板的实例形成重载函数;函数没有特化和偏特化

template<typename T, typename... Types>
void print(const T &firstArg, const Types &... args) {
// firstArg是包中第一个元素的引用,输出包中剩余元素个数
cout << sizeof...(args) << " ";
cout << firstArg << endl;
print(args...); // 当args为空时,调用的是非模板函数print
}

可变模板参数实现hash function

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
template<typename T>
inline void hash_combine(size_t &seed, const T &val) {
seed ^= std::hash<T>()(val) + 0x9e3779b9 // 0x9e3779b9黄金比例
+ (seed << 6) + (seed >> 2);
}

template<typename T>
inline void hash_val(size_t &seed, const T &val) {
hash_combine(seed, val);
}

template<typename T, typename...Types>
inline void hash_val(size_t &seed, const T &val, const Types &...args) {
hash_combine(seed, val);
hash_val(seed, args...);
}

// 版本1
// typename...Types:任意个模板参数
// 第一个函数不是size_t
template<typename...Types>
inline size_t hash_val(const Types &...args) {
size_t seed = 0;
hash_val(seed, args...);
return seed;
}

可变模板参数实现printf

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
void printf(const char *s) {
while (*s) {
if (*s == '%' && (*(++s) != '%')) {
throw std::runtime_error("invalid formal string::missing arguments");
}
cout << *s++;
}
}

template<typename T, typename... Args>
void printf(const char *s, T value, Args... args) {
while (*s) {
if (*s == '%' && (*(++s)) != '%') {
cout << value;
printf(++s, args...);
return;
}
cout << *s++; // 如果s不是用于格式控制,则直接输出s
}
throw std::logic_error("extra argument provided to printf");
}

void test_printf() {
printf("%d\n%s\n%f\n", 15, "This is Alice", 3.1415926);
printf("%d, %s, %f\n", 15, "This is Alice", 3.1415926);
}

variadic templates与initializer_list

若参数的类型相同,无须使用variadic templates,使用initializer_list<T>即可

使用initializer_list实现max

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
31
32
33
34
template<typename __ForwardIterator, typename __Compare>
__ForwardIterator __max_element(__ForwardIterator __first, __ForwardIterator __last, __Compare _comp) {
if (__first == __last) {
return __first;
}
__ForwardIterator __result = __first;
while (++__first != __last) {
if (_comp(__result, __first))
__result = __first;
}
return __result;
}


// _Iter_less_iter是一种类型

inline __Iter_less_iter __iter_less_iter() {
return __Iter_less_iter();
}

template<typename __ForwardIterator>
inline __ForwardIterator max_element(__ForwardIterator __first, __ForwardIterator __last) {
// _iter_less_iter()是一个函数调用,但是返回一个对象
return __max_element(__first, __last, __iter_less_iter());
}

template<typename _Tp>
inline _Tp
max(initializer_list<_Tp> _l) {
return *max_element(_l.begin(), _l.end());
}

// 调用形式:
max({1,2,43,2,41});

使用varadic templates实现max

1
2
3
4
5
6
7
8
9
10
11
12
int maximum(int n) {
return n;
}

template<typename... Args>
int maximum(int n, Args... args) {
return max(maximum(args...), n);
}

void test_maximum() {
cout << maximum(57, 48, 60, 100, 20, 18) << endl;
}

对异于一般的方式处理first元素和last元素

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
template<int IDX, int MAX, typename... Args>
struct PRINT_TUPLE {
static void print(ostream &os, const tuple<Args...> &t) {
os << get<IDX>(t) << (IDX + 1 == MAX ? "" : ",");
PRINT_TUPLE<IDX + 1, MAX, Args...>::print(os, t);
}
};


// 当前元素是最后一个
template<int MAX, typename...Args>
struct PRINT_TUPLE<MAX, MAX, Args...> {
static void print(ostream &os, const tuple<Args...> &t) {}
};

template<typename... Args>
ostream &operator<<(ostream &os, const tuple<Args...> &t) {
PRINT_TUPLE<0, sizeof...(Args), Args...>::print(os, t);
return os;
}


void test_print_tuple() {
cout << make_tuple(1.4, "hello", 13, string("good")) << endl;
}

可变模板参数实现tuple

通过private 继承实现复合

递归的继承

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
template<typename...Values>
class tuple;

template<>
class tuple<> {
};


// private继承
template<typename Head, typename... Tail>
class tuple<Head, Tail...> : private tuple<Tail...> {
typedef tuple<Tail...> inherited;

protected:
Head m_head;
public:
tuple() {}

// inherited(vtail...)调用base ctor并给予参数(不是创建temp object)
tuple(Head v, Tail... vtail) : m_head(v), inherited(vtail...) {}

// 原始构想是取出Head指定的类型,但是head类型如何知道自己是什么type?
// typename Head::type head() { return m_head; }

// 通过decltype获取Head
// m_head必须声明在此句之前
// auto head() -> decltype(m_head) { return m_head; }

// 更好的等价写法
// 可以直接这么写,Head本身就是指的类型
// Head head() { return m_head; }

// return后转型为inherited
// 返回值一定得是引用
// 调用tail之后,得到一个inhertied子对象,然后再对此子对象调用head时会改变此子对象,但是如果传回的是值(而非引用),则被改变的是副本
inherited &tail() { return *this; }
};

void test_tuple() {
tuple<int, float, string> t(41, 6.3, "nico");
cout << sizeof(string) << " " << sizeof(int) << " " << sizeof(float) << endl; // 32 4 4
cout << sizeof(t) << endl; // 40
cout << t.head() << endl; // 40
cout << t.tail().head() << endl; // 6.3
cout << t.tail().tail().head() << endl; // nico
}

1569046889935

通过composite实现复合

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 这两句存在的意义是什么?换言之,为什么需要这两句
template<typename... Values>
class tup;

// 特化版本
template<>
class tup<> {
public:
tup() {
cout << "empty tup" << endl;
}
};


// 特化版本
template<typename Head, typename...Tail>
class tup<Head, Tail...> {
typedef tup<Tail...> compositied;
protected:
compositied m_tail;
Head m_head;
public:
tup() {};

// vtail...是一个函数参数包 
tup(Head v, Tail... vtail) : m_head(v), m_tail(vtail...) {
cout << m_head << " " << sizeof...(vtail) << endl;
}

Head head() { return m_head; }

compositied &tail() { return m_tail; }
};

void test_tup() {
tup<int, float, string> it1(41, 6.3, "nico");

cout << endl;

cout << sizeof(it1) << endl;
cout << it1.head() << endl;
cout << it1.tail().head() << endl;
cout << it1.tail().tail().head() << endl;
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
# 由内到外构造对象
empty tup
nico 0
6.3 1
41 2

# 依次输出tup中的内容
56
41
6.3
nico

1569204035682