写在前面
在array类源码看到这样一段代码
template<class _First,
class... _Rest>
array(_First, _Rest...)
-> array<typename _Enforce_same<_First, _Rest...>::type, 1 + sizeof...(_Rest)>;
于是决定深入了解一下c++变长参数的用法。
变长参数
可变参数是c++11的新特性,它允许函数的输入参数为不确定个,通常用“ ... ”代替。
void fun(int start, ...)
像上述代码这样,就声明了一个可变参数的函数。它以start为首,放入不确定个数的int类型的参数。
先来看一个代码
#include <iostream>
#include<stdarg.h>
using namespace std;
void fun(int start, ...) {
va_list args;
va_start(args, start);
int arg = start;
while (arg != -1) {
cout << arg << " ";
arg = va_arg(args, int);
}
va_end(args);
}
int main()
{
fun(1, 2, 3, 4, 5, 6, 7, 8, 9, -1);
return 0;
}
解释:
在main函数中的fun函数的-1参数是截止标志(可变参数不知道会有几个参数被传入,所以要手动设置结束方式,一般第一个参数为参数个数,或者最后一个参数为结尾标志,本次使用后者)
观察fun函数我们发现,访问可变参数使用了以下变量和方法(这些都在stdarg.h这个头文件中):
-
va_list
-
void va_start( va_list arg_ptr, prev_param );
-
type va_arg( va_list arg_ptr, type );
-
void va_end( va_list arg_ptr );
va_list是声明了一个可变参数的列表,它使用va_start()进行初始化,并指定首个数值,va_arg()是访问下一个参数,类似于链表的next,最后使用va_end()释放内存等杂七杂八的东西;
变长参数的实现
va_list
首先打开va_list的源码,它的定义如下:
typedef char* va_list;
可以看出va_list是一个char型指针。
va_start()
打开va_start(),我们发现他是重名了 __crt_va_start
#define va_start __crt_va_start
继续追踪,发现以下代码
__crt_va_start(ap, x)
((void)(__vcrt_assert_va_start_is_not_reference<decltype(x)>(), __crt_va_start_a(ap, x)))
__vcrt_assert_va_start_is_not_reference<decltype(x)>()的意思估计是如果start不是一个引用就断言,实现如下:
extern "C++"
{
template <