C++可变参数使用总结

简介: C++可变参数使用总结

可变参数是指函数可以接受不定数量的参数。比如在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

相关文章
|
4月前
|
安全 JavaScript Java
C++11:可变参数模板
C++11:可变参数模板
|
5月前
|
存储 C++
【C++】可变参数模板使用总结(简洁易懂,详细,含代码演示)
【C++】可变参数模板使用总结(简洁易懂,详细,含代码演示)
|
5月前
|
存储 编译器 C语言
【C++11特性篇】模板的新一力将:可变参数模板 [全解析]
【C++11特性篇】模板的新一力将:可变参数模板 [全解析]
|
8月前
|
编译器 C++
【c++11】新的类功能和可变参数包
【c++11】新的类功能和可变参数包
|
7月前
|
机器学习/深度学习 编译器 C++
C++11:类的新功能和可变参数模板
C++11:类的新功能和可变参数模板
53 1
|
5天前
|
算法 编译器 C++
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
22 1
|
2月前
|
存储 安全 C语言
C++中灵活 处理可变参数的多种方式:优缺点、应用场景及示例对比
C++中灵活 处理可变参数的多种方式:优缺点、应用场景及示例对比
76 1
|
2月前
|
存储 安全 C++
【C++ 泛型编程 高级篇】C++可变参数模板探索:编程技巧与实战应用
【C++ 泛型编程 高级篇】C++可变参数模板探索:编程技巧与实战应用
57 0
|
2月前
|
存储 安全 C语言
不只是printf:探究C/C++语言中的可变参数函数
不只是printf:探究C/C++语言中的可变参数函数
28 0
|
2月前
|
安全 编译器 程序员
【C++ 泛型编程 进阶篇】C++ 可变参数模板的妙用:解决参数不足问题
【C++ 泛型编程 进阶篇】C++ 可变参数模板的妙用:解决参数不足问题
38 0