跟我学c++中级篇——C++14中的透明操作符
一、透明操作符
Transparent function objects,透明函数对象,也可以叫透明操作符或透明比较器。名字看上去很高大上,其实很好理解,所谓透明操作符,其实就是一种特殊类型的函数对象,它可以接受任何类型的参数同时不会进行隐式的类型转换。
说得更简单一些,就是不需要指定函数对象操作的参数数据类型却可以自动推导出其类型。透明操作符其实也是经常提到的标准向简单方便的方向发展的一种趋势。在确保安全、准确的前提下,越简单越好。
二、透明操作符说明和特性
透明操作符自C++14开始支持,而透明则意味着开发者减少了对数据类型的强制控制,既达到了对复杂数据类型的准确控制又减少了开发中的复杂度。
1、重要性
与老的C++11标准而言,使用透明操作符,可以将关于数据类型的强数据类型的编写转为简单的自动推导从而减少编码的复杂度。比如:
// C++11
std::sort(v.begin(), v.end(), std::greater<int>());
// C++14
std::sort(v.begin(), v.end(), std::greater<>());
使用透明操作符,可以加强数据类型的安全使用,让代码变得更清晰并易于维护,这才是对开发者最友好的地方。
2、缺点和限制
凡一事有优点即会有缺点,对于透明操作符来说,其主要的缺点为:
a)增加了编译的复杂性
和模板编程或元编程相似,对数据类型的自动推导本身就是一个复杂的情况,出现问题时,也相对较难于查找定位
b)透明操作符的应用具有限定性
不是任何一种操作符都可以做为透明操作符,同样,有些场景下使用透明操作符也未必强于普通操作符
c)需要处理新旧代码的融合
这个就非常容易理解了,老的代码和新的代码如何共用、共生是一个很大的问题,这就需要相关的责任人认真处理
3、主要的透明操作符
在C++14中常见的几个透明操作符如下:
std::less<>,std::greater<>,std::less_equal<>,std::greater_equal<>,std::equal_to<>,std::not_equal_to<>。这几个运算符非常简单,不用过多说明。
对于透明操作符来说,其有几个明显的特征:
1、类型透明性:可以接受不同类型的参数进行比较
2、避免不必要的构造:不需要为了比较而构造临时对象
3、性能优化:在某些情况下可以减少类型转换的开销
其实通过上面的分析可以看到,透明操作符在模板编程和元编程中,更容易实现一些复杂的应用场景。
三、透明操作符的原理
1、类型的自动推导
透明操作符的优秀之处在于减少了模板相关编程中的数据类型的主动处理,它使用前面分析过得自动推导技术来实现相关的数据类型的确定。
2、编译器中的处理
对于使用透明操作符,编译器其实是增加了不少的工作量的。但让前端降低难度而增加后面服务相关的难度这也是目前编程框架的普遍趋势。当然如果二者都能降低更好,但这种事情往往比较难于做到。
3、与隐式转换的不同
其实这种情况更容易理解,人们既喜欢应用上的简单,但实际应用又想显式的把控整个脉络,这或许就是人们的普遍的矛盾的心理吧。隐式转换其实是一种退让型的简便,而透明操作符其实是一种符合C++标准的显式控制。所以在标准的演进过程中,一定趋向于显式的安全控制。在这种基础上再在应用上简单,更容易让标准的大牛们接受。
四、应用例程
透明操作符既可以使用标准库中的相关操作符也可以进行自定义,看一下相关的例程:
#include <iostream>
#include <functional>
#include <set>
#include <string>// customize
struct DicCompare {template<typename T, typename U>bool operator()(T&& lhs, U&& rhs) const {return std::lexicographical_compare(std::forward<T>(lhs).begin(), std::forward<T>(lhs).end(),std::forward<U>(rhs).begin(), std::forward<U>(rhs).end(),[](char a, char b) {return std::tolower(a) < std::tolower(b);});}
};int main() {//setstd::set<std::string, std::less<>> strSet;strSet.insert("YES");strSet.insert("NO");bool ret1 = strSet.find("YES") != strSet.end(); // true//bool ret2 = strSet.find(1) != strSet.end(); //compile errstd::cout << std::boolalpha << "found YES: " << ret1 << std::endl;std::set<std::string, DicCompare> dSet;dSet.insert("tree");dSet.insert("plane");bool ret = dSet.count("TREE") > 0; // case-insensitivestd::cout << "found tree: " << ret << std::endl;return 0;
}
其实这种透明操作符的应用与原来的显示指定数据类型的应用没有质的不同,对强类型的指定是C++本身的特点。所以,不管标准如何进步,其仍然没有超出C++语言本身的限制。
五、总结
科技以人为本,对C++标准而言,更是如此。做为一种普遍认为相对较为复杂,编程难度较高的语言来说,不断的将相关的开发难度降低或者说不断的调整C++编程的难度向更容易理解、更方便应用的方向发展,是一个必然的过程。