C++可变参数
C++可变参数是指函数的参数个数是可变的,可以在函数定义时不确定参数的个数,需要在函数体内通过特定的语法来处理这些参数。C++11标准引入了新的可变参数模板,使得可变参数的处理更加方便和灵活。在函数定义时,可以使用省略号(…)来表示可变参数,然后通过va_list、va_start、va_arg和va_end等宏来访问这些参数。例如,下面是一个简单的可变参数函数的示例:
#include <iostream>
#include <cstdarg>
using namespace std;
double average(int count, ...)
{
va_list ap;
int i;
double sum = 0;
va_start(ap, count); // 初始化可变参数列表
for (i = 0; i < count; i++)
{
sum += va_arg(ap, double); // 访问可变参数列表中的参数
}
va_end(ap); // 结束可变参数列表
return sum / count;
}
int main()
{
cout << average(3, 1.0, 2.0, 3.0) << endl; // 输出平均值
return 0;
}
在上面的示例中,average函数的第一个参数count表示可变参数的个数,后面的省略号表示可变参数列表。在函数体内,首先使用va_start宏初始化可变参数列表,然后使用va_arg宏访问可变参数列表中的参数,最后使用va_end宏结束可变参数列表。
C++可变参数具体可参见:C语言函数参数中的三个点(三点 “…”)是干什么用的?(可变参数)<stdarg.h>、va_start 宏、va_arg 宏、va_end 宏
C++可变参数模板
C++11标准引入了新的可变参数模板,使得可变参数的处理更加方便和灵活。可变参数模板是指模板参数个数是可变的,可以在模板定义时不确定参数的个数,需要在模板实例化时通过特定的语法来处理这些参数。
可变参数模板的语法形式为:
template <typename... Args>
void func(Args... args)
{
// 函数体
}
其中,typename... Args
表示模板参数包,可以包含任意个数的模板参数,args表示函数参数包,可以包含任意个数的函数参数。在函数体内,可以使用sizeof...(Args)
来获取模板参数包中参数的个数,使用sizeof...(args)
来获取函数参数包中参数的个数。
示例(可变参数模板、递归参数模板、C++17折叠表达式)
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
// ------------------------------------------------------------C++17折叠表达式
template <typename... Args>
void foldPrint(Args... args)
{
std::cout << "The number of template arguments is: " << sizeof...(Args) << std::endl; // The number of template arguments is: 4
std::cout << "The number of function arguments is: " << sizeof...(args) << std::endl; // The number of arguments is: 4
#if 0
// C++17折叠表达式对可变函数参数的处理
std::cout << "The arguments are: ";
(std::cout << ... << args) << std::endl; // The arguments are: 12helloc
#endif
}
// ------------------------------------------------------------递归函数模板
// 基本模板函数
template <typename T>
void recursionPrint(T t)
{
std::cout << t << std::endl;
}
// 特化版本,用于处理空参数包的情况
// template <>
// void recursionPrint<>(int t)
// {
// std::cout << t << std::endl;
// }
// 递归模板函数(当参数还剩一个时,就会调到上面那个基本模板函数那儿)
template <typename T, typename... Args>
void recursionPrint(T t, Args... args)
{
std::cout << t << ", "; // 不endl不会刷新缓冲区
recursionPrint(args...);
}
// ------------------------------------------------------------C++11 可变参数模板和 C++17 折叠表达式
template <typename... Args>
void variablePrint(Args... args)
{
int arr[] = {(std::cout << args << ", ", 0)...}; // 这段代码是使用了 C++11 中的可变参数模板和折叠表达式,可以将多个参数打印出来并存储在数组中。
// 具体来说,代码中的 args 是一个可变参数模板,表示可以接受任意数量的参数。
//(std::cout << args << ", ", 0) 是一个折叠表达式,表示对于每个参数,先将其输出到标准输出流 std::cout 中,然后再输出一个逗号和一个空格,并返回 0。
// 这样,对于每个参数,都会输出到标准输出流中,并返回 0。
// 最后,使用花括号将所有参数的输出结果存储在一个数组 arr 中,这个数组的类型是 int[],因为折叠表达式中返回的是 0,所以数组中的每个元素都是 0。
// 需要注意的是,这段代码的输出结果中,每个参数之间都会有一个逗号和一个空格,最后一个参数后面也会有一个逗号和一个空格。
// 如果需要去掉最后一个参数后面的逗号和空格,可以在输出数组元素时进行特判。
std::cout << std::endl;
}
int main()
{
// C++17折叠表达式
std::cout << "C++17折叠表达式" << std::endl;
foldPrint(1, 2.0, "hello", 'c');
std::cout << std::endl;
// 递归函数模板
std::cout << "递归函数模板" << std::endl;
recursionPrint(1, 2.0, "hello", 'c'); // 1, 2, hello, c
std::cout << std::endl;
// 可变参数模板
std::cout << "C++11 可变参数模板和 C++17 折叠表达式" << std::endl;
variablePrint(1, 2.0, "hello", 'c'); // 1, 2, hello, c,
std::cout << std::endl;
return 0;
}
运行结果:
可变参数模板的使用方式有两种,递归展开和参数包展开
递归展开
递归展开是指在函数模板内部使用递归调用来处理可变参数,直到处理完所有参数。例如,下面是一个递归展开可变参数模板的示例:
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
template <typename T>
void print(T t)
{
cout << t << endl;
}
template <typename T, typename... Args>
void print(T t, Args... args)
{
cout << t << ", ";
print(args...);
}
int main()
{
print(1, 2.0, "hello"); // 输出1, 2, hello
return 0;
}
在上面的示例中,print函数模板使用递归调用来处理可变参数,首先输出第一个参数t,然后递归调用print函数模板来处理剩余的参数args。
参数包展开(只支持C++17)
参数包展开是指使用特定的语法来展开参数包,将参数包中的参数逐个传递给函数。例如,下面是一个参数包展开可变参数模板的示例:
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
#include <iostream>
using namespace std;
template <typename... Args>
void print(Args... args)
{
cout << sizeof...(args) << endl; // 输出参数个数
(cout << ... << args) << endl; // 展开参数包
}
int main()
{
print(1, 2.0, "hello"); // 输出3和12hello
return 0;
}
在上面的示例中,print函数模板使用sizeof...(args)
来获取参数个数,使用(cout << ... << args)
来展开参数包,将参数包中的参数逐个传递给cout输出。
为什么template <typename... Args>模板参数中...放Args前面,void printSizeOfArgs(Args... args)中...放Args后面?
template <typename... Args>
中的...
放在Args
前面是因为它表示一个模板参数包,用于接收任意数量的模板参数。而void printSizeOfArgs(Args... args)
中的...
放在Args
后面是因为它表示一个函数参数包,用于接收任意数量的函数参数。
在模板参数列表中,...
表示一个参数包,它可以接收任意数量的模板参数。例如,template <typename... Args>
中的Args
可以接收任意数量的模板参数,比如Args<int>
、Args<int, double>
、Args<char, bool, float, double>
等等。
在函数参数列表中,...
表示一个参数包,它可以接收任意数量的函数参数。例如,void printSizeOfArgs(Args... args)
中的args
可以接收任意数量的函数参数,比如printSizeOfArgs(1)
、printSizeOfArgs(1, 2, 3)
、printSizeOfArgs("hello", 'a', 3.14)
等等。
因此,...
放在Args
前面和后面的含义是不同的,它们分别表示模板参数包和函数参数包。
模板参数和函数参数有什么区别?
模板参数和函数参数的区别主要有以下几点:
- 类型不同
模板参数是用于定义模板的类型、常量或模板的类型参数,它们是在编译时确定的。而函数参数是用于传递函数调用时的实际参数,它们是在运行时确定的。
- 作用域不同
模板参数的作用域是整个模板,可以在模板的任何地方使用。而函数参数的作用域只在函数内部有效,不能在函数外部使用。
- 默认值不同
模板参数可以有默认值,如果没有显式指定模板参数的值,编译器会使用默认值。而函数参数也可以有默认值,但是只能在函数声明中指定,不能在函数定义中指定。
- 数量不同
模板参数的数量可以是任意的,可以定义任意数量的模板参数。而函数参数的数量是固定的,必须在函数声明中指定。文章来源:https://www.toymoban.com/news/detail-720067.html
- 实例化不同
模板参数是在模板实例化时确定的,每个模板实例化都会生成一个新的类型或函数。而函数参数是在函数调用时确定的,每次函数调用都会使用相同的函数定义,但是传递的参数可以不同。文章来源地址https://www.toymoban.com/news/detail-720067.html
到了这里,关于C++11可变参数模板(typename... Args模板参数包或class... Args)(Args... args函数参数包)(递归展开与参数包展开(只支持C++17))的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!