介绍 C++ lambda、仿函数、std::function 等。
仿函数
仿函数(functor),实际上是一个定义了运算符operator()的类。
1 | struct Foo |
仿函数不是函数,std::is_function返回false_type。
Functor Adaptor
std::function的实现
《STL源码解析》一书首先介绍了诸如binary_function、unary_function之类的,注意到里面诸如argument_type、result_type的成员都在C++17中被deprecated了,原因是现在C++中的完美转发和decltype已经能够很好地解决这个问题了,所以我们不要计较这个细节。<functional>中还定义了诸如plus、minus、multiplies、divides、modulus、negate等数学函数和诸如equal_to、less等比较函数,我们常见是用法是作为一个predicate,也就是sort(v.begin(), v.end(), greater<int>());,此外还实现了一些函数式的函数,例如identity、select(类似car或者head)、project等。
类型擦除
说到std::function的实现,必须要提一下类型擦除的机制。以std::any这样的机制为例,它可以储存任何形式的对象,在C++17之后由于有了构造函数的模板推导,事情不太一样了,可以直接用个模板,然后加一些乱七八糟的casting。
1 | template <typename T> |
不过不考虑这些,因为假设std::any是一个非模板类,那么现在就面临如何存储的问题,一种方法是用void *,不过这样会丢失类型信息。
1 | struct my_any { |
于是引入一层间接,也就是将实际盛放T的模板类holder<T>继承一个placeholder,于是就可以通过操作placeholder来操作holder<T>了。与此对应的机制还有智能指针实现中著名的T: public enable_shared_from_this<T>,具体可以查看我的博文
1 | class placeholder |
std::function中的类型擦除
为什么std::function中会需要类型擦除呢?首先我们知道C++中的Callable或者说Invokable的对象主要有函数指针、函数对象和lambda几类,而这三个的类型不尽相同,lambda更是每一个lambda都具有一个类型。