假設我想寫一個支持變長參數的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;
}
且看方案一的強制轉換部分和初始化部分,相較而言,方案三好太多。