變長參數表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;
}

且看方案一的強制轉換部分和初始化部分,相較而言,方案三好太多。

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