加载中...
C++模板可变参数如何一次性解包?
第1节:代码改善:一个“坑爹”的文字类冒险游戏
第2节:在禁止多重继承的情况下,如何设计“直立智慧猩猩”类?
第3节:C++多线程代码中的“乱序”执行现象
第4节:C++中函数指针有什么作用呢?
第5节:为什么我用c++写的游戏那么简陋?
第6节:多线程读写socket导致的数据混乱的原因是什么?
第7节:WebSocket 是什么原理?为什么可以实现持久连接?
第8节:怎样在c++中实现instanceof?
第9节:一个函数多处 return 是好风格吗?
第10节:C++中虚函数相比非虚函数的优势
第11节:为什么 C::C::C::C::foo() 能编译成功?
第12节:如何静态反射C++枚举的名字
第13节:看C++大叔如何拥 java 妹子入怀……
第14节:坨——理解递归实现“汉诺塔”代码的关键
第15节:C++编译器如何实现 const(常量)?
第16节:C++如何为断言加上消息
第17节:初学C++到什么水平,算是合格的初级开发工程师?
第18节:C++编程要避免使用单例模式吗?
第19节:学习C++要学boost库吗?
第20节:C++的继承就是复制吗?
第21节:C++构造函数失败,如何中止创建对象?
第22节:C++学完多线程后,学什么呢?
第23节:string_view 适合用做函数的返回值类型吗?
第24节:为指针取别名,为何影响const属性?
第25节:std::enable_shared_from_this 的存在意义?
第26节:C++模板可变参数如何一次性解包?
第27节:Linux下的c++开发,平时是怎么调试代码的呢?
课文封面

简单看懂如下代码:

template <typename T1, typename ... T2s> void func1(T1 a, T2s ... rest) { std::cout << a; (std::cout << ... << rest) << "\n"; }

三处的 … 你都看懂并能使用了吗?

1 问题

问:如下代码中,调用func1的时候,只能递归一次取到一个值,有没有什么方法像func2中那样,一次把所有的值都拿出来呢?

问题-如何一次性取出C++参数包中的所有参数

2 回答

先了解与此相关的C++三个基本知识点:

  • 二元操作 (binary operator) 」 :需要两个操作数的操作,比如加法操作: a+b,再如输出流操作 : cout << ‘a’ ;二者分别对应 + 操作符和 << 操作符。
  • 参数包 (parameter pack) 」: “正经”的函数模板的入参数是一个 伪类型,再加一个伪形参名,比如: T1 t1 。 参数包 则在二者中间加一个小三…,比如 T1 … t1。
  • 然后,C++17 的“二元操作”,支持其中一个使用 “参数包/pack”。编译器遇到二者,就会自动从“包”中取出一个参数,作为眼下就要进行的二元操作的一个操作数,进行操作。取参数次序还可以区分正序(从左到右排列)或倒序(从右到左排列)。

2.1 你要的“一次性取出”的例子

template <typename T1, typename ...T2s> // #1 void func1(T1 a, T2s ... rest) // #2 { std::cout << a; // #3 (std::cout << ... << rest) << "\n"; // #4 rest:余下的 }

2.2 例子代码解读

  • #1:typename 和 T1 之间干干净净,而 typename 和 T2s 之间夹着小三点,所以T1是一个正经的模板参数,但T2s是一个模板参数包;它可能是0个或1个或更多个参数类型;
  • #2:T1和a之间干干净净,而T2 和 rest 之间夹着 小三点,所以 a 是一个参数,而 … 和 rest 在此共同表示 一个包(参数包)。
  • #3:先是 std::cout 正经地输出了正经的入参 a;
  • #4:然后遇上参数包, “(cout << … << rest)”,理解为 << (包中第1个参数);即首先从“包”中取眼下的第1个参数(如果有)作为 操作数2 ,与固定的操作数1(就是cout),进行 “<<” 运算——其实就是“输出”—— 然后,再取包中剩下的,如此反复,直至把参数包掏空。
  • 继续上一点:这里 “…” 和 具体的参数名(本例中的 rest )在语法与语义表达上有了分工:“…”表示包中的当前第一个,而 具名参数 rest 表示:“请继续如此处理包中余下的参数……”。另外 ,外围的括号( ) 是必须的,表示在 #4 这行完整的语句中,参与和参数包有关的求值的准确范围(一个表达式);所以 << “\n” 只会在参数包处理完成后执行只执行一次。事实上,哪怕把 << “\n” 从代码中删除,括号也得留着。

注:小括号(或称圆括号)在C++中,并不仅仅用于改变优先级,并且在优先级不改变的情况下,有时候它也不能省略。更非可有可无。括号可强制令相关代码成为一个表达式。这点和 一元的 + (俗称正号)在某些地方,有和没有完全不同(有+号,会让修饰的代码变成表达式,典型如将一个 lambda 变成一个 表达式,最终转换成 函数指针的做法,以后有空再说)。

2.3 调用示例

main() { func1(12, " + ", 13, " == 25"); }

将输出: 12 + 13 == 25。

空包调用示例:

func1("Hello world!"); // 此时,形参 ... rest 是个空包

2.4 支持的“原生”的二元操作符

共32个:

+ - * / % ^ & 
| = < > << >>
+= -= *= /= %= ^= &= |= <<= >>= == != <= >=
&& || , .* ->*

趁热练练手?请往下滚动屏幕……