C++随笔:可变参数
灵知子
编辑于 2024年05月31日 14:59
收录于文集
共33篇

➤ 可变参数

    (1)可变参数函数,以符号 “...” 作为可变参数函数的标识,可变参数一定是参数列表中最后一个位置,参数个数一般以参数列表首个参数传入,否则不知道什么时候该结尾,当然也可通过其它方式,比如 printf 函数中搜索%的个数来确定参数个数。大致原理为:从参数首地址开始,因为必须指定每个参数类型,所以可由此计算出地址运算的范围,返回正确的对应类型的地址。

代码块
C++
自动换行
复制代码
#include <stdarg.h>
void func1(unsigned int argsNum, ...)
{
    va_list args;
    va_start(args, argsNum);
    
    // 参数列表中的参数都是同一类型
    for (unsigned int idx = 0; idx < argsNum; ++idx)
    {
        cout << va_arg(args, int) << endl;
    }
    
    // 参数列表中的参数,也可以是不同类型,不过就需要一一指定了
    cout << va_arg(args, int) << endl;
    cout << va_arg(args, const char*) << endl;
    cout << va_arg(args, float) << endl;
    
    va_end(args);
}
复制成功

    (2)可变参数宏,和可变参数函数类似,也采用符号 ... ,作为宏定义左侧的可变参数标识,而宏定义右侧则采用内置宏 __VA_ARGS__,可理解为将 ... 所传入的参数列表,替换到 __VA_ARGS__ 的位置。

代码块
C++
自动换行
复制代码
#define MyPrint(...) printf(__VA_ARGS__)
int main()
{
    // 输出:Print: Hello world ~ 666
    MyPrint("Print: %s ~ %d", "Hello world", 666);
    return 0;
}
复制成功

    (3)可变参数模板,在函数模板的基础上,进一步定义可变参数。相比于可变参数函数,在可变参数模板可以通过特定的运算符 sizeof... 获取参数列表中参数的个数,此运算符只能在可变参数模板所定义的函数中使用。初始化参数列表常用于容器的初始化,如vector大括号初始化。这里可变参数用参数包来描述,解参数包有一般有两种方式:

    ① 递归法:标准的解可变参数包的方法,需要定义一个接受0个参数的终止函数。需要注意的是,在递归过程中,使用 sizeof... 运算符得到的是当前调用时参数的数量,每执行以此递归,将消耗一个参数。

代码块
C++
自动换行
复制代码
// 同名的无参数终止函数,在递归结束后调用
void func()
{
    	cout << "Analysis done.\n" << endl;
}

template<typename FirstArg, typename... MultiArgs>
void func(FirstArg firstArg, MultiArgs... args)
{
    // 得到此次递归调用时,剩余的参数数量
    unsigned int leftArgsNum = sizeof...(args);
    // 递归依次输出:
    // Cur arg: 111    Left args count:3
    // Cur arg: 222    Left args count:2
    // Cur arg: 333    Left args count:1
    // Cur arg: 444    Left args count:0
    // Analysis done.
	cout << "Cur arg: " << firstArg << "\tLeft args count:" << leftArgsNum << endl;
	func(args...);
}

int main()
{
	func(111, 222, 333, 444);
}
复制成功

    ② 初始化列表方法:C++11中的型特性,初始化参数列表,std::initializer_list结合逗号表达式,可非递归的解参数包。

代码块
C++
自动换行
复制代码
template<typename... MultiArgs>
void func(MultiArgs... args)
{
    // 得到参数的数量
    	unsigned int argsNum = sizeof...(args);
    // 用一个容器保存参数列表
    vector<int> vecOb;
    // 这个 666 可能是为了保证逗号表达式的语法,具体用意不明,这里可以是任何(或可隐式转换为)int 类型的数据
    std::initializer_list<int>{ (vecOb.push_back(args), 666)... };
    for (vector<int>::iterator itr = vecOb.begin(); itr != vecOb.end(); ++itr)
		cout << *itr << ','; // 输出:111,222,333,444
    cout << endl;
    // 因为vector本身也可以用初始化列表来初始化,所以可以直接获取参数列表
    vector<int> vecArgs{ args... };
    for (vector<int>::iterator itr = vecArgs.begin(); itr != vecArgs.end(); ++itr)
        cout << *itr << ','; // 输出:111,222,333,444
}

int main()
{
	func(111, 222, 333, 444);
}
复制成功

特别注意:应该尽可能的避免使用可变参数,如果函数需要多个参数,优先考虑重载。