STL源码分析binder2nd

简介

binder2nd是一个函数适配器,这篇文章想通过一个函数调用对这个adapter进行详细的分析。

分析

我们的主函数是一条语句,计算数组中比50小的元素个数。

1
cout << count_if(vec.begin(), vec.end(), bind2nd(less<int>(), 50));

上面这段代码中count_if是主角,因此对它先进行下研究。

1
2
3
4
5
6
7
8
9
10
11
12
// count_if 模板
template <class InputIterator, class Predicate>
typename iterator_traits<InputIterator>:: difference_type
count_if(InputIterator first, InputIterator last, Predicate pred)
{
typename iterator_traits<InputIterator>::difference_type n = 0;
for (; first != last; ++first)
{
if (pred(*first)) ++n;
}
return n;
}

注意到pred应该是一个函数对象,要能够对参数*first进行判断。其返回类型是difference_type定义了一长串。

首先看下less模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// less 模板
template <class Arg1, class Arg2, class Result>
struct binary_function
{
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
}
template <class T>
struct less: public binary_function<T,T,bool>
{
bool operator()(const T &x, const T &y) const
{
return x < y;
}
}

less模板继承了二元function,因此有了三个类型名,可以回答算法的提问。less() 是一个可调用函数对象,需要两个函数参数。下面主要看如何进行参数绑定的。
先看看bind2nd的源代码:

1
2
3
4
5
6
template <class operation, class T>
inline binder2nd<operation> bind2nd(const Operation &op, const T& x)
{
typedef typename Operation::second_argument_type arg2_type;
return binder2nd<Operation>(op, arg2_type(x));
}

毫无疑问bind2nd是一个函数模板,在调用该函数时首先进行函数模板参数推断,得到operation就是less, 而op就是一个可调用对象less()。这样,由于less继承了binary_function,所以less::second_argument_type就是int。注意typedef typename中的typename是用来告诉编译器这是个类型,从而能够编译通过。bind2nd()函数模板返回的是一个可调用函数对象binder2nd(),与前面看过的count_if模板相对应。下面看看binder2nd类模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<class Operation>
class binder2nd: public unary_function<typename Operation::first_argument_type,
typename Operation::second_argument_type>
{
protected:
Operation op;
typename Operation::second_argument_type value;
public:
// 构造函数
binder2nd(const Operation &x, const typename Operation::second_argument_type &y): op(x), value(y){}
typename Operation::result_type
operator()(const typename Operation::first_argument_type &x) const
{
return op(x, value);
}
}

在上述类模板中有一个构造函数分别对对象的op与value进行了赋值,这样就构建了一个可调用的函数对象,也就是count_if中的pred。注意其返回值是typename Operation::result_type即bool类型的,与其对应。

总结

应该说这个函数调用关系还是比较复杂的,复杂的原因是让设计更简单,便于代码重复使用。只要慢慢弄懂每个对象的含义,整体的感觉是设计还是很精妙的。

声明

若有错误,欢迎讨论。严禁抄袭,仅用于学习。

坚持原创技术分享,您的money将鼓励我继续创作!