省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包

1.C语言的va_list展开可变参数
cpp 复制代码
#include <iostream>
#include <cstdarg>

void printNumbers(int count, ...) {
    // 声明va_list类型的变量
    va_list args;
    // 使用va_start将可变参数写入变量args
    va_start(args, count);
    for (int i = 0; i < count; i++) {
        // 调用va_arg依次获取可变参数的参数值
        int value = va_arg(args, int);
        std::cout << value << " ";
    }
    // 使用va_end清理变量args
    va_end(args);
    std::cout << std::endl;
}

int main() {
    printNumbers(3, 1, 2, 3); // 输出: 1 2 3
    printNumbers(5, 1, 2, 3, 4, 5); // 输出: 1 2 3 4 5
}

使用va_list感觉还挺麻烦的,需要提前知道参数个数,并且还得需要知道入参类型。因此不如直接使用数组了。

2.C++的initializer_list展开可变参数
cpp 复制代码
#include <iostream>

template <typename T>
void printNumbers(std::initializer_list<T> nums) {
    // 获取可变参数长度为
    std::cout << "可变参数长度为:" << nums.size() << std::endl;
    std::cout << std::endl;
    // 遍历输出可变参数,方法1
    for (const T& i: nums) {
        std::cout << i << " ";
    }
    std::cout << std::endl;
}

int main() {
    printNumbers({"1","qq"});
    printNumbers({1, 2, 3});
}
3.可变参数模板--递归函数方式展开可变参数
cpp 复制代码
#include <iostream>

using namespace std;

void print() {
    cout << endl;
}

template <typename T> 
void print(const T& t) {
    cout << t << endl;
}

template <typename First, typename... Rest> 
void print(const First& first, const Rest&... rest) {
    cout << first << ", ";
    print(rest...); // recursive call using pack expansion syntax
}

int main()
{
    print(); // calls first overload, outputting only a newline
    print(1); // calls second overload

    // these call the third overload, the variadic template,
    // which uses recursion as needed.
    print(10, 20);
    print(100, 200, 300);
    print("first", 2, "third", 3.14159);
}

使用这种方法,可变参数可以是任意类型的,不需要都统一是一个类型了。

这种方法需要提供一个参数包展开的函数和一个递归终止函数,二者同名。递归终止函数的参数可以为0,1,2或者多个(一般用到0个或1个),当参数包中剩余的参数个数等于递归终止函数的参数个数时,就调用递归终止函数,则函数终止。

4.可变参数模板--折叠表达式展开可变参数
cpp 复制代码
#include <iostream>

template<typename... Args>
void prints(Args... args) {
    //注意args在前,省略号在后否则报错
    //std::cout <<sizeof...(args)<<std::endl;//sizeof...() 运算符可获取参数包参数个数
    ((std::cout << args << " "), ...); // 折叠表达式展开参数包
    std::cout << std::endl;
}

int main() {
    prints("test", 2, 'k');   // 输出: test 2 k
    prints(1, 2, 3, 4, 5);   // 输出: 1 2 3 4 5
}

折叠表达式是C++17 才引入的新特性,使用折叠表达式可以很方便实现展开可变参数,但是可读性也会大打折扣。在此使用的是折叠表达式的逗号运算符。折叠表达式还会分为左折叠和右折叠。

5.可变参数模板--结合std::tuple展开可变参数
cpp 复制代码
#include <iostream>
#include <tuple>

// 终止条件:当索引等于元组大小时
template<size_t I = 0, typename Tuple>
typename std::enable_if<I == std::tuple_size<Tuple>::value, void>::type
printtp(const Tuple&) {
    // 空实现,递归终止
}

// 递归展开:当索引小于元组大小时
template<size_t I = 0, typename Tuple>
typename std::enable_if<I < std::tuple_size<Tuple>::value, void>::type
printtp(const Tuple& t) {
    std::cout << std::get<I>(t) << std::endl;  // 打印当前元素
    printtp<I + 1>(t);                         // 递归调用下一个索引
}

// 包装函数:将参数包转换为元组并调用printtp
template<typename... Args>
void print(Args... args) {
    printtp(std::make_tuple(args...));
}

int main() {
    print(1, "hello", 3.14);  // 输出:1\nhello\n3.14
    return 0;
}

在这使用了std::tuple数据结构以及类型萃取中常用的std::enable_if。其实本质上还是递归模板参数展开,用起来可能不如直接使用上述方法三。

本文参考:

https://zhuanlan.zhihu.com/p/684295744

C++17常用新特性(十一)---折叠表达式-腾讯云开发者社区-腾讯云

省略号和可变参数模板 | Microsoft Learn

https://zhuanlan.zhihu.com/p/731232067

C++中神奇的tuple:详解使用技巧和实例解析-腾讯云开发者社区-腾讯云

C++之std::enable_if_std enable if-CSDN博客

c++11------可变参数模板 - 农民伯伯-Coding - 博客园

相关推荐
冻柠檬飞冰走茶32 分钟前
字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)
c语言·开发语言·数据结构·算法·动态规划
FRing031 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2
雪落漂泊1 小时前
C++算法 二分查找篇(上)
开发语言·c++·算法
波力海苔夹心脆6752 小时前
C# WPF 资源字典详解:ResourceDictionary、MergedDictionaries 与自定义控件案例
开发语言·c#·wpf·入门教程·resource·资源字典
不会算法的笨小孩2 小时前
异常的使用以及会出现的问题
开发语言·c++·c++11·异常
LabVIEW开发3 小时前
LabVIEW 连接板:28 个端子怎么排,优先级怎么定
java·算法·labview
严肃小白兔4 小时前
MATLAB 数值计算中的精度与误差问题:浮点运算、误差传播与数值稳定性
人工智能·算法·matlab
Hhy_11074 小时前
《C++深度解构06》手撕 string —— 从底层内存管理到深浅拷贝彻底吃透
c语言·开发语言·c++·学习·visual studio
starzy19905 小时前
深入理解Scala函数定义:从方法到函数的类型跨越
开发语言·scala
starzy19905 小时前
深入理解Scala递归函数:从栈溢出到尾调用优化
开发语言·scala