一、为什么需要可变参数
在前面的可变参数的相关文章学习中,已经基本了解了可变参数是如何应用的。特别对于其内部实现更是有一个全面的认知。但C++为什么要提供可变参数模板呢?其实如果不是计算机技术的进步,编程技术还是限制于单机的单进程间,那么可变参数出现的意义并不多大。
但随着跨进程甚至是跨计算机的分布式服务的出现,各种参数及数量的变化,特别是不同语言甚至非语言间的交互,使得参数传递变得复杂异常。面对这种情况,XML及JOSN的出现和普及虽然从表面上弥合了大部分的参数传递的鸿沟,但对于C++语言来说,仍然存在着各种不确定的东西。
二、可变参数面临的问题
C++作为一种强类型的语言,它天然的要求在编译时必须搞定各种参数的类型、数量等具体的参数特征。但分布式的开发中,互相传递的参数往往是在程序运行时才进行参数的打包的。举一个简单的例子,网络通信中,通过JSON进行动态打包的参数,其数据类型和数量会被处理到一个序列化的字符串中。或者大家可能在后来的开发中看到std::any或std::variant之类的容器或数组参数列表中。虽然从抽象角度的看,开发者似乎是保证了C++的基础的要求,但实际上,在真正的运行时应用时需要对参数进行解析,这就无法满足大多数的应用场景下的需求。
从这个问题的角度来看,也是C++语言天然对Web开发(特别是前端)不太友好的一个主要原因。但进步不是你想不想,而是必须的。所以C++也必须解决这个问题。虽然可能解决这个问题仍然不如其它语言友好,可只要是有这种解决的想法就是一种进步。
解决矛盾的思路看上去简单,但是在实际实现时,对于C++语言来说,还是相当有难度的。它要求不但能够满足C++在编译时的强类型要求,保证数据类型的安全性;同时又必须动态运行时的实际参数与编译时生成的参数的正确匹配。
三、变参及C++标准的演进支持
对于C++这种强类型的语言来说,变参包处理时需要重点解决的几个问题包括:参数的类型、参数传递的顺序、参数数量以及CV限定符等的处理。开发者遇到变参最初可能是从C语言的printf系列开始,从而认识va_list。但C语言中的va_list存在着较为严重的问题,最主要的就是类型的安全保证的问题。而这恰恰是现代C++最不能容忍的问题。另外,C++中还可以使用模板的不同的参数进行不同的特化行为版本。这个可能不搞模板开发的不太清楚,但如果说宏定义就比较明白了。使用宏来完成多个参数类型的展开也是非常常见的(参看前面的反射相关文章)。如果还是不能理解,还可以看早期的一些MFC中的API,它会把几乎开发者能想到参数类型及数量的都重载一遍。
这样做的局限性和风险大家能够显而易见的发现。而C++11引入了变参模板,则较好的解决了这些问题。首先,在编译期,包括参数类型安全控制、参数数量检查以及参数引用折叠等主要的问题都被安全控制。特别是完美转发可以很好的控制参数类型的自动处理,保证变参模板在参数解包过程中的安全性(包括CV限定符的处理)。其次,变参模板可以通过折叠表达式和递归等方式实现参数的运行时的动态解包。特别是在C++26中引入的参数包索引(Pack indexing),让解包变得更加可控。
四、分布式应用中的变参
在上面提到的问题中,分布中的变参处理才是一个重要的棘手的问题。C++提供了变参的基础控制后,如何在分布式应用中进行相关的参数匹配呢?解决的方法与变参模板的发展机制类似:
- 完全由开发者自定义
即开发者对变参的数量和类型进行控制,将所有的可能都编写一次。如果需要增加,则变参组包和解包两个方向都要进行相应的处理。这样做的优势就是看上去实现简单,就好像写重载函数一样,照着抄就行。但缺点就是完全没有灵活性。代码的调试和应用都变得复杂。 - 显式的提供变参组包和解包的逻辑
这个有点类似于大家领福利,只要把自己装福利的包(组包)交给发福利的人(注册)装福利进去,然后回家后,自己再打开包(解包)就可以了。它的优势就非常明显了,可以非常完美的使用C++新标准中的变参模板(包括一系列的auto的操作)。从而既让开发者省心也让调用者省心。唯一麻烦的是增加了一层逻辑的封装。
这种情况看上去很复杂,其实大家可能经常遇到,比如注册进去一个变参模板函数为回调函数,在其中进行相关的参数解包处理就可以了。这样,任何第三方都可以使用这种逻辑来处理开发者的自定义变参包了。 - 使用SFINAE+模板元编程
这种方式在早期的库中应用十分广泛,但它的缺点在于编程的复杂度非常高,对开发者的水平要求也相当高。同时不同平台和不同编译器往往对其支持的并不完全,导致其可靠性和可移植性变得不可控制。而且元编程的难于调试也是一个重要的问题,往往让很多开发者即使是水平很高的开发者也觉得头疼。总结成一点就是可维护性大大降低。 - 使用反射
这才是终极的大杀器,但C++不是暂时搞不定么。
在不断的扩展视野和学习进步的过程中,大家会发现,解决问题的思路和方法有很多。这也是反复提到的思路要灵活,方法要紧扣实际问题的原因。解决问题往往只有最合适的而没有最优的。只要明白了变参技术的发展,那么在看到或解决类似的问题时,才会有针对性提出解决问题的方法而不是简单的照猫画虎,最后弄个画虎不成,各方都觉得难受的结果。
四、总结
对于很多技术的初学者,学习技术本身并不是多么复杂和有难度的事。但问题是,这些人往往无法明白这门技术为什么而来。这就如同为什么很多人在网上推荐初学者使用国外的教材的一个重要原因。知其然不知其所以然,对技术的理解经常会浮于表面。正如现在流行的一句话:“人教人学不会,事儿教人一次就会”。只有真正的把技术的来龙去脉弄清楚,才能明白技术演进的思想,真正把技术与实践结合起来。从而在自己的实践过程中减少不必要的弯路。
技术一定是来源于实践的,但技术又会指导后来人不用走前人经过的各种困难和问题。所以后来者学习时,也要明白这一点,否则就可能重走技术来源的路。

691

被折叠的 条评论
为什么被折叠?



