假设我想写一个支持变长参数的max函数。
template <typename T>
T max(T first_arg, ...)
{
if (first_arg == ???) // Q1.
return ???; // Q2.
va_list ap;
va_start(ap, first_arg);
T tmp, ret_val = ???; // Q3.
while((tmp = va_arg(ap, T)) != ???) // Q4.
{
if (tmp > ret_val)
ret_val = tmp;
}
va_end(ap);
return ret_val;
}
关于变长参数如何确定长度,官方给出两个方案:
h2. 1. 第一个参数为参数数量,这个很容易想到。(见示例代码max)
h2. 2. 最后一个参数为特殊字符,就像字符数组以\0结尾那样。
对于第二个方案,这个特殊字符到底得特殊到什么程度,很难确定。
对int类型来讲,可以定为INT_MIN
对double类型来讲,可以定为DOUBLE_MIN
我们知道变长参数表,需要这样定义
va_list ap;
va_start(ap, first_arg);
接着我们就要去遍历这个ap了,何时遍历结束呢?
while (T tmp = va_arg(ap, T))
{
if (tmp == ???)
break;
}
也不保证,max只有一个参数(也就是结束符)
那又得怎么判断 first_arg == ??? 返回什么值呢?
综上所述,对于变长参数表的max函数,第二个方案不太可行。
我们要弃用while,改用更安全的for循环,也就是说, 在遍历之前,就已经能确定比较次数。
不过,template的模板形参,不仅支持参数类型,而且支持参数数值,在这里实际上还有第三种方案:
h2. 3. 使用template,模板实参指定传入参数数量。(见示例代码min)
完整示例代码如下
#include <iostream>
#include <limits.h>
#include <stdarg.h>
using namespace std;
template <typename T>
T max(T n, ...)
{
int len = static_cast<int>(n); // 强制转换很无奈
va_list ap;
va_start(ap, n);
T ret_val = T(INT_MIN); // 这个INT_MIN初始化很无奈
for (T i = 0; i < len; ++i)
{
T tmp = va_arg(ap, T);
if (tmp > ret_val)
ret_val = tmp;
}
va_end(ap);
return ret_val;
}
template <int len, typename T>
T min(T first, ...)
{
va_list ap;
va_start(ap, first);
T ret_val = first;
for (T i = 0; i < len - 1; ++i) // 因为len是参数总数,第一个参数已经给了ret_val,所以这里的比较次数-1
{
T tmp = va_arg(ap, T);
if (tmp < ret_val)
ret_val = tmp;
}
va_end(ap);
return ret_val;
}
int main()
{
cout << max(3,1,2,4) << endl;
cout << min<4>(9, 2, 6, 4) << endl; // 模板实参参数类型甚至可以省略,这里函数实例根据参数确定为int
cout << min<4, int>(9, 2, 1, 4) << endl;
cout << max(3.0, 1.6, 2.8, 4.9) << endl; // 第一个参数一定要写成3.0,不能写成3(将后续参数类型约束为double类型)
cout << min<4>(0.9, 2.2, 6.3, 4.7) << endl;
return 0;
}
且看方案一的强制转换部分和初始化部分,相较而言,方案三好太多。