变长参数表va_list,模板template,打造通用函数

假设我想写一个支持变长参数的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;
}

且看方案一的强制转换部分和初始化部分,相较而言,方案三好太多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章