跳至主要內容

C++

LincZero大约 3 分钟

C++

目录

可变参数模板 Variadic Templates

本篇参考:

可变参数函数

三个点...的写法以前就有,但不是在模板里

printf 源码

int __cdecl printf (
	const char *fromat,
	...	// 可以接受任意变量
	)
{
    // ...
}

可变参数模板

可变参数模板 函数递归

例程 - print

/** 模板代码 */
void print()
{
    
}

template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args)	// 表示参数的类型和数目都可变
{
    cout << firstArg << endl;
    print(args...);
}

/** 使用代码 */
print(7.5, "hello", bitset<16>(377), 42);

/* 输出:
7.5
hello
0000000101111001(16bit来表示377)
42
*/

Q:

template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args){/**/}
// 和
template <typename... Types>
void print(const Types&... args){/**/}
// 可以共存吗
// 若可,谁比较泛化、谁比较特化

A:

// 可以共存
// 优先使用比较特化的那个版本,即
template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args){/**/}
// 的版本

原理

...就是一个pack包

  • 用於template parameters,就是template parameters pack(模板参数
  • 用於function parameter types,就是function parameter types pack(函数参数类型
  • 用於function parameters,就是function parameters pack(函数参数

可以帮助我们做递归,以上述例程为例:

(7.5, "hello", bitset<16>(377), 42)传入后会被分割成两块firstArg = 7.5args = ("hello", bitset<16>(377), 42)

然后重复这个过程递归调用,当args为0时,就调用print()的重载版本

优先级

class CustomerHash{
public:
    std::size_t operator()(const Customer& c) const {
        return hash_val(c.fname, c.lname, c.no)		// 调用【版本1】(只有版本1符合)
    }
};

可变参数模板函数的重载

// 【版本1】
template <typename... Type>
inline size_t hash_val(const Types&... arg){		// 接受一个参数
    size_t seed = 0;
    hash_val(seed, args...);						// 调用【版本2】(版本1、2、3均符合)
    return seed;
}

// 【版本2】
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...);						// 调用【版本2】,递归到最后调用【版本3】
}

// 【版本3】
template<typename T>
inline void hash_val(size_t& seed, const T& val) {	// 接受两个参数
    hash_combine(seed, val);
}

#include <functional>
template <typename T> 
inline void hash_combine(size_t& seed, const T& val){
	seed ^ = std:hash<T>O(val) + 0x9e3779b9
		+ (seed<<6) +(seed>>2);
}

可变参数模板 类递归

例程 - tuple

tuple头文件

template<typename... Values> class tuple;
template<> class tuple<>{};

tuple代码

template<typename Head, typename... Tail>
class tuple<Head,Tail...> :private tuple<Tail...> 	// 方便递归,自己继承自己【绝了,妙啊】
{
    typedef tuple<Tail...> inherited;
public:
	tuple(){ }
	tuple(Head v, Tail... vtail)
        :m_head(v), inherited(vtail...){}			// initialization list【关键】抽出来的部分定义为成员变量,其他部分给父类

    typename Head::type head(){ return m_head; }
    inherited& tail(){ return *this; }				// 返回指针
protected:
	Head m_head;
};

调用代码

tuple<int, float, string> t(41, 6.3, "nico");
t.head();			// 获得41
t.tail().head();	// 6.3

应用

应用方向

可变参数模板主要用于递归

可以很方便地完成 recursive function call(递归函数调用)

在标准库中的使用

(详见C++11标准库一节)