C++ functor 和 lambda

介绍 C++ lambda、仿函数、std::function 等。

仿函数

仿函数(functor),实际上是一个定义了运算符operator()的类。

1
2
3
4
5
6
struct Foo
{
void operator()(const int & x)
{
}
};

仿函数不是函数,std::is_function返回false_type

Functor Adaptor

std::function的实现

《STL源码解析》一书首先介绍了诸如binary_functionunary_function之类的,注意到里面诸如argument_typeresult_type的成员都在C++17中被deprecated了,原因是现在C++中的完美转发和decltype已经能够很好地解决这个问题了,所以我们不要计较这个细节。
<functional>中还定义了诸如plusminusmultipliesdividesmodulusnegate等数学函数和诸如equal_toless等比较函数,我们常见是用法是作为一个predicate,也就是sort(v.begin(), v.end(), greater<int>());,此外还实现了一些函数式的函数,例如identityselect(类似car或者head)、project等。

类型擦除

说到std::function的实现,必须要提一下类型擦除的机制。以std::any这样的机制为例,它可以储存任何形式的对象,在C++17之后由于有了构造函数的模板推导,事情不太一样了,可以直接用个模板,然后加一些乱七八糟的casting。

1
2
3
4
5
6
7
template <typename T>
struct my_any {
typedef T value_type;
my_any(const T & raw){
...
}
}

不过不考虑这些,因为假设std::any是一个非模板类,那么现在就面临如何存储的问题,一种方法是用void *,不过这样会丢失类型信息。

1
2
3
4
5
6
7
struct my_any {
void * ptr = nullptr;
template <typename T>
my_any(const T & raw){
...
}
}

于是引入一层间接,也就是将实际盛放T的模板类holder<T>继承一个placeholder,于是就可以通过操作placeholder来操作holder<T>了。与此对应的机制还有智能指针实现中著名的T: public enable_shared_from_this<T>,具体可以查看我的博文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class placeholder 
{
virtual ~placeholder(){}
virtual const std::type_info & type() const = 0;
virtual placeholder * clone() const = 0;
};

template<typename T>
class holder : public placeholder
{
holder(const T & value): held(value) {}
virtual const std::type_info & type() const {
return typeid(T);
}
virtual placeholder * clone() const {
return new holder(held);
}
public:
T held;
};

std::function中的类型擦除

为什么std::function中会需要类型擦除呢?首先我们知道C++中的Callable或者说Invokable的对象主要有函数指针、函数对象和lambda几类,而这三个的类型不尽相同,lambda更是每一个lambda都具有一个类型。