C++可变参数使用总结

2023-05-16

可变参数是指函数可以接受不定数量的参数。比如在printf函数,如果做日志等功能也会用到。这里总结下可变参数的使用。

 c语言中的可变参数

先介绍下在c语言中的使用,c语言也支持可变参数,只是需要借助下va_list和va_arg宏解析。

使用方式一

//省略符形参应该仅仅用于C和C++通用的类型
int sum(int count, ...){
    if (count <= 0)
	{
		return 0;
	}
	//为了实现可变参数列表,首先需要声明一个va_list类型的指针
	//va_list类型是在cstdarg头文件里面定义的,该指针用来依次指向各个参数
	//va_start是一个宏,用来初始化arg_ptr,使其指向列表的第一个参数,这个宏的第二个参数是sum函数参数列表省略号前得固定参数的名称,用来确定第一个参数的位置    
	va_list arg_ptr;
	va_start(arg_ptr, count);

	int CountSum = 0;
	//va_arg是一个宏,返回arg_ptr指向的参数位置,并使arg_ptr递增来指向下一个参数值
	//va_arg宏的第二个参数是需要统计的第一个参数的类型,如果类型不正确,
	//程序也可能会执行,但得到的是无用的数据,arg_ptr将被错误地递增
	for (int i = 0; i < count; ++i)
	{
		CountSum += va_arg(arg_ptr, int);
	}
	//将va_list类型的指针复位成空值就是清空可变参数列表
	va_end(arg_ptr);
	return CountSum;
}

//....
sum(5,1,2,3,4,5);//return 15

使用方式二

宏定义,__VA_ARGS__ 就表示可变参数列表;

#define debug(...) print(cout,__VA_ARGS__)
#define debug2(os, ...) print(os, __VA_ARGS__)
//#define debug2(format, args...) print(format, ##args)//win32不支持

debug2(cout,"this ", " year");//"this  year"

 c++模板可变参数

模板可变参数使用格式定义,举例如下:

template<typename T, typename... Args>
void foo(const T& t, const Args&... rest) {
	std::cout << sizeof(T) << endl;
	cout << sizeof...(Args) << endl;//打印可变参数数量
	cout << sizeof...(rest) << endl;//打印可变参数数量
}

如何一个一个的打印出参数?在c++之前,这里有个递归的实现方法。在c++17之后,也可以不用递归,有更简单的写法,可以使用c++17提供的折叠表达式fold expression,让我们处理变参更简单,代码也会变得更加简洁。注折叠表达式用法只能在c++17及以上的编译环境下使用,否则会收到如下编译错误:

main.cpp: In function ‘int foo(Args ...)’:
main.cpp:21:26: warning: fold-expressions only available with ‘-std=c++17’ or ‘-std=gnu++17’
   21 |     return (foo1(args) + ...);
      |                          ^~~

先介绍下递归的实现方法:

//2个参数时 比后者更特例化,会优先匹配此函数,
//必须写在前面,不然会递归编译到print(ostream& os)从而编译错误
template<typename T, typename... Args>
ostream& print(ostream& os, const T& t) {
	return os << t << ", ";
}

template<typename T,typename... Args>
ostream& print(ostream& os, const T& t, const Args&... rest) {
	os << t << ", ";
	return print(os, rest...);
}

print(cout,1,2,"haha");//"1, 2, haha"

 c++17的fold expression的实现方法:

#include <iostream>

using namespace std;

template<typename T,typename... Args>
ostream& print(ostream& os, const T& t, const Args&... rest) {
	os << t;
	((os << ","<<rest),...);
	os << "\n";
	return os;
}

int main()
{
    cout<<"Hello World\n";
    print(cout,1,2,"haha");//"1, 2, haha"
    return 0;
}

 泛型lambda表达式

template<typename T>
int foo1(T t)
{
    return t;
}
template <typename... Args>
int foo(Args ...args)
{
    return (foo1(args) + ...);
}
void TestCpp17(std::string a,std::string b)
{
    auto f1 = []<typename T, typename N>(T t, N n)-> T {
        return t + n;
    };
    auto f2 = []<typename ...T>(T && ...args) {
        return foo(std::forward<T>(args)...);
    };

    std::string s = f1(a,b);
    std::cout << "string is:" << s << std::endl;

    int d = f2(1,2,3,6);
    std::cout << "result is:" << d << std::endl;

}
int main()
{
    std::string s1 = "123";
    std::string s2 = "321";
    std::string ss = s1 + s2;
    TestCpp17(s1,s2);
    return 0;
}

可变参数模板 

以下示例仅在c++17之后可用。使用了可变参数模板,使得overloaded可以继承自多个Lambda。其次使用了Using-declaration,以防止重载之时产生歧义。类模板实参推导 (CTAD),C++17的CTAD(Class Template Argument Deduction),以推导出overloaded的类型。这样做的目的,就是通过CTAD为overloaded添加一个用户自定义的类型推导指引,从而让编译器可以推导lambda的类型,进而可以创建出overloaded类型的对象。

#pragma once
#include <iostream>
//可变参数模板
template < typename ... Ts > 
struct Animal:Ts ... 
{
  //使用Using-declaration,以防止重载之时产生歧义
  using Ts::operator () ...;
};

//c++17的类模板实参推导 (CTAD)
template < typename ... Ts > 
Animal (Ts ...)->Animal < Ts ... >;

struct Pig { void display() { std::cout << "is pig" << std::endl; } };
struct Horse { void display() { std::cout << "is horse"<< std::endl; } };

// 定义
static constexpr auto Factory = Animal{
 []<typename T>(const T& t) { return new T; }
 };
void TestFactory()
{
    auto pig = Factory(Pig{});
    pig->display();
}
int main()
{
    TestFactory();
    return 0;
}

可变参数initializer_list

这种方式局限性很大,列表里只能同类型,他的用法跟传vector参数类似,但是比vector跟轻量,而且元素是常量。

//initializer_list相对vector更轻量化 而且元素是常量
void error_msg(initializer_list<string> il) {
	for (auto beg = il.begin(); beg != il.end(); ++beg)
		cout << *beg << " ";
	cout << endl;
}

error_msg({"im","da","gong"});//"im da gong"

引用

C++17 fold expression_恋喵大鲤鱼的博客-CSDN博客_fold expression

C++17尝鲜:fold expression(折叠表达式)_zwvista的博客-CSDN博客

巧用C++17的fold expression - 墨天轮

泛型lambda表达式

C++ Shell

现代 C++ 教程: 高速上手 C++ 11/14/17/20 - Modern C++ Tutorial: C++ 11/14/17/20 On the Fly 

C++17新特性_青山白云间的博客-CSDN博客_c++17新特性

GDB online Debugger | Compiler - Code, Compile, Run, Debug online C, C++

Class template argument deduction (CTAD) (since C++17)_Jeff_的博客-CSDN博客_ctad msvc

C++11、C++14、C++17、C++20新特性总结(5万字详解)_小熊coder的博客-CSDN博客_c++ 11 14 17 20

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++可变参数使用总结 的相关文章

随机推荐

  • Qt笔记-pro文件中添加-fpermissive

    老项目里面的库 xff0c 一包含就报 fpermissive错误 在Makefile里面写比较方便 在Qt的pro里面要这也 xff1a QMAKE CXXFLAGS 43 61 fpermissive 我在Linux上成功了 如果不行可
  • Python文档阅读笔记-Car detection with OpenCV

    在人脸检测中 xff0c 使用预先被训练过的模型文件去侦测脸和眼睛 同样使用预先被训练过来的模型文件去检测汽车 将用这个模型检测出来的汽车用矩形框出来 代码如下 xff1a OpenCV Python program to detect c
  • Arduino文档阅读笔记-4 WHEEL ROBOT CAR BASIC EXAMPLE

    Arduino小车有很多种 xff0c 包括2个轮子 xff0c 4个轮子 都有 下面这个实例来做一个最基础的4轮小车 xff0c 下面将说明各个功能和代码 首先得先准备这样的一辆4个轮子的小车 再选这样的一个扩展板 xff1a 电机控制板
  • Qt文档阅读笔记-Tetrix Example解析

    本文的俄罗斯方块实例官方的一个经典实例 俄罗斯方块这个游戏就是从顶部落下方块到底部 xff0c 把每行都填充满 当一行被填充满 xff0c 这行就会被移除 xff0c 玩家就会获取分数 顶部的方块都会依次落下 xff0c 如果有多行被填充满
  • Linux笔记-使用grep检索指定字符串

    以前都是用find和grep这种组合命令去检索 find name 34 cpp 34 xargs grep i main find name 34 34 xargs grep i main 这种更加灵活点 xff0c 但是有时用grep就
  • C/C++文档阅读笔记-A Simple Makefile Tutorial解析

    Makefile文件可以使得程序编译变得简单 本博文并不是很系统的讲解makefile xff0c 本博文的目标是让读者快速编写自己的makefile文件并能应用到中小项目中 简单实例 举个例子有下面3个文件 xff0c 分别是hellom
  • Phaser笔记-Hello World!

    代码如下 xff1a lt DOCTYPE html gt lt html gt lt head gt lt script src 61 34 https cdn jsdelivr net npm phaser 64 3 15 1 dist
  • 如何实现ubuntu虚拟机与windows系统之间复制粘贴

    在使用ubuntu虚拟机时 xff0c 有时需要在虚拟机与windows系统之间进行文件或者字符串的复制和粘贴 比如 xff1a 某个文件在主机上 xff0c 但是开发时需要在虚拟机中使用 xff0c 需要把这个文件导入到虚拟机中 其实这个
  • C/C++笔记-记录一次对qmake生成的Makefile的分析(2023-02-07)

    如下Qt代码 xff1a ConsoleDemo pro QT 43 61 core QT 61 gui TARGET 61 ConsoleDemo CONFIG 43 61 console CONFIG 61 app bundle TEM
  • Phaser笔记-scene中的preload、create、update、player、键盘控制

    一般phaser最简单的配置文件如下 xff1a let config 61 type Phaser AUTO width 800 height 600 scene preload preload create create update
  • C/C++笔记-写一个Makefile并链接QtCore库使用QString,QDebug

    如下cpp代码 xff0c 调用QDebug打印程序 xff1a include lt QDebug gt include lt QString gt int main int argc char argv QString testStr
  • C/C++笔记-分析带有Q_OBJECT继承QObject的类make流程

    此篇博文记录到个笔记时间2023 02 15 xff0c 发表到网上的时间是2023 05 03 这里以Qt5 5 1为例 xff0c 操作系统是centos 7 5版本 代码如下 xff1a MOCQtConsole pro QT 43
  • C/C++笔记-构造makefile使用MOC程序及编译,链接

    背景是这样的构造一个Test类 xff0c 使用了Q OBJECT宏 xff0c 和继承了QObject xff0c 也就是元对象 xff0c 这时 xff0c makefile需要如何写 如下代码 xff1a Test h ifndef
  • C++20新特性个人总结

    目录 1 关键字 1 1 concept 1 2 requires 1 3 typename 1 4 explicit 1 5 constexpr 1 6 char8 t 1 7 consteval 1 8 co await co yiel
  • MySQL | MySQL为什么建议不要使用视图

    MySQL为什么建议不要使用视图 一 什么是视图二 为什么不建议使用视图 一 什么是视图 官方定义 xff1a 视图是指计算机数据库中的视图 xff0c 是一个虚拟表 xff0c 其内容由查询定义 同真实的表一样 xff0c 视图包含一系列
  • 【数据标准】学院数据集标准规范-1.6-元数据XML

    XML概述 XML 基础教育教学资源元数据的数据模型定义了一个层次结构的概念模型 xff0c 层次结构的模型可以方便地表示包含许多元素和子元素的数据 XML非常适合表示层次结构的模型 XML文档就是层次结构的 xff0c 它由元素组成 xf
  • Python-音频处理之wav文件与十进制pcm文件的相互转换

    一 pcm转wav 1 pcm文件以十进制的格式保存 xff0c 因此读取pcm的txt文件时不用 rb 来读取 2 适用于写单通道wav文件 xff0c 多通道需要对数据进行reshape操作 import wave import str
  • webapp打包为Android的apk包的一种方法

    开发了个纯web的app小demo 想着最终集成到微信公众号上做个小功能 xff0c 并测试下如何跨平台的运行在Android上 关于如何打包为Android的安装包 xff0c 总结了下其中的一种方法 xff0c 使用cordova打包为
  • 滤波算法(二)—— 中位值滤波算法

    1 算法介绍 中位值滤波算法的实现方法是采集N个周期的数据 xff0c 去掉N个周期数据中的最大值和最小值 xff0c 取剩下的数据的平均值 中位值滤波算法特别适用于会偶然出现异常值的系统 中位值滤波算法应用比较广泛 xff0c 比如用于一
  • C++可变参数使用总结

    可变参数是指函数可以接受不定数量的参数 比如在printf函数 xff0c 如果做日志等功能也会用到 这里总结下可变参数的使用 c语言中的可变参数 先介绍下在c语言中的使用 xff0c c语言也支持可变参数 xff0c 只是需要借助下va