1. 配接器简介
配接器(adapters)在 STL 组件的灵活组合上,扮演者轴承、转换的角色。Adapters事实上是一种设计模式(design pattern)。
百度百科对adapters模式的解释:
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。——Gang of Four
在STL的体系中,有三种类型的adapters:
1. Container adapters:应用于容器的配接器。STL提供的两个容器queue和stack,实际上就是配接器,它们底层都是用的 deque ,通过修饰 deque 的接口而形成另一种风貌;
2. Iterator adapters:应用于迭代器的配接器,这是本文介绍的主角;
3. function adapters:应用于仿函数(函数对象)的配接器;
本文介绍函数对象的配接器实现方法,下文中所述的function adapters 指的就是函数对象的配接器。
2.function adapters
2.1 什么是function adapters
function adapters 的价值在于,通过它们之间的绑定、组合、修饰能力,几乎可以无限制地创造出各种可能的表达式。
比如我们希望找出某个序列中所有不小于 12 的元素个数,可以这样做:
not1(bind2nd( less<int>(), 12 ) )
上式将 less<int>() 的第二个参数绑定为 12,再加上否定操作,便形成了“不小于 12 ”的语义,整个凑合成一个表达式,可与任何“接受表达式为参数”的算法搭配。
由于函数对象就是“将 operator() 重载”的一种 class,而任何算法接受一个函数对象时,总是在其验算过程中调用该函数对象的 operator(),这使得不具备函数对象之形、却有真函数之实的“一般函数”和“成员函数”感到为难,为此 STL 又提供了为数众多的配接器,使得“一般函数”和“成员函数”得以无缝接的与其他配接器或算法结合起来。
请注意,所有期望获得配接能力的组件,本身必须是可配接的(adaptable),换句话说,一元函数必须继承自 unary_function,二元函数必须继承自 binary_function,成员函数必须以 mem_fun 处理,一般函数必须以 ptr_fun 处理。
2.2 function adapters 的工作原理
一般而言,对于 C++template 语法有了一定的了解之后,我们很能理解或想象,容器以 class template 完成、算法以 function template 完成、函数对象是一种将 operator() 重载的 class template,迭代器则是一种将 operator++ 和 operator* 等指针惯常行为重载的class template。
function adapters 的工作原理,就像容器配接器(container adapters)内藏了一个 container member 一样,或是像迭代器配接器(iterator adapters)内藏了一个 iterator member 一样,每一个 function adapters 也内藏了一个 member object ,其类型等同于它所要配接的对象(这个对象显然是一个“可配接的函数对象”,adaptable function object)。当 function adapters 有了完全属于自己的修饰对象(member object),function adapters 就有资格调用该修饰对象(一个函数对象),并在参数和返回值上动手脚了。
下图对 count_if() 搭配 bind2nd(less<int>(), 12 ) 的例子,其中清楚的显示了 count_if() 的控制权是怎么落到我们手上的,控制权一旦在我们手上,我们当然可以予取予求了。
3.代码实现
我用VS2013写的程序(github),function adapters的实现版本的位于cghSTL/version/cghSTL-0.7.3.rar
本文介绍的函数对象需要以下文件:
1. cgh_function_adapters.h,本文的主角,function adapters的实现,位于cghSTL/adapters/
2. cghStl_algo.h,function adapters 是配合着算法使用的,测试iterator adapters用到的算法包括 count_if()、for_each()、transform(),这些算法均在cghStl_algo.h中实现。cghStl_algo.h位于cghSTL/algorithms/
3. cgh_function_obj.h,function adapters 的主要作用是配接函数对象,测试 function adapters 用到的函数对象包括 less<int>()、plus<int>()、multiplies<int>(),这些函数对象均在cgh_function_obj.h中实现,cgh_function_obj.h 位于 cghSTL/function objects/
4. cghVector.h,自己实现的 vector,关于 vector的实现,请移步:STL 简单 vector 的实现。vector用于测试,cghVector.h 位于 cghSTL/sequence containers/cghVector/
5. test_function_adapters.cpp:测试function adapters,位于cghSTL/test/
所有的function adapters的代码都在cgh_function_adapters.h文件中,cgh_function_adapters.h位于cghSTL/adapters/,我用region 把cgh_function_adapters.h文件划分成多个模块,可以看到一共实现了八个function adapters。
function adapters的实现如下,有疑问的地方我都做了注释,童鞋们可以根据注释来理解其工作原理。
cgh_function_adapters.h
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* chengonghao@yeah.net
*
* 功能:函数配接器的实现
******************************************************************/
#ifndef _CGH_FUNCTION_ADAPTERS_H_
#define _CGH_FUNCTION_ADAPTERS_H_
#include "cghStl_funcbase.h"
namespace CGH{
#pragma region cgh_unary_negate
template<class predicate>
class cgh_unary_negate : public unary_function < typename predicate::argument_type, bool >
{
protected:
predicate pred; // 内部成员
public:
explicit cgh_unary_negate(const predicate& x) : pred(x) {}
bool operator()(const typename predicate::argument_type& x) const
{
return !pred(x); // 将 pred 的运算结果加上否定(negate)运算
}
};
/* 以下函数返回一个匿名的 cgh_unary_negate 对象,这使得我们可以以函数调用的方式使用函数对象 */
template<class predicate>
inline cgh_unary_negate<predicate> cgh_not1(const predicate& pred)
{
return cgh_unary_negate<predicate>(pred);
}
#pragma endregion
#pragma region cgh_binary_negate
template<class predicate>
class cgh_binary_negate : public binary_function<typename predicate::first_arguement_type,
typename predicate::second_argument_type,
bool>
{
protected:
predicate pred;
public:
explicit cgh_binary_negate(const predicate& x) : pred(x) {}
bool operator()(const typename predicate::first_arguement_type& x,
const typename predicate::second_argument_type& y) const
{
return !pred(x, y); // 将 pred 的运算结果加上否定(negate)运算
}
};
/* 以下函数返回一个匿名的 cgh_binary_negate 对象,这使得我们可以以函数调用的方式使用函数对象 */
template<class predicate>
inline cgh_binary_negate<predicate> cgh_not2(const predicate& pred)
{
return cgh_binary_negate<predicate>(pred);
}
#pragma endregion
#pragma region cgh_binder1st
/* 以下配接器可以将某个 Adaptable Binary function(二元函数)转换为 Unary Function(一元函数)*/
/* 具体的做法是让一元函数绑定二元函数的第一个参数,忽略第二个参数 */
template<class operation>
class cgh_binder1st :public unary_function < typename operation::second_argument_type,
typename operation::result_type >
{
protected:
operation op; // 对参数的操作方式
typename operation::first_argument_type value; // 二元函数的第一个参数值
public:
cgh_binder1st(const operation& x, const typename operation::first_argument_type& y) :op(x), value(y) {}
// operation::result_type:返回值类型
typename operation::result_type operator()(const typename operation::second_argument_type& x) const
{
return op(value, x);
}
};
/* 以下函数返回一个匿名的 cgh_binder1st 对象,这使得我们可以以函数调用的方式使用函数对象 */
template<class operation, class T>
inline cgh_binder1st<operation> cgh_bind1st(const operation& op, const T& x)
{
typedef typename operation::first_argument_type arg1_type; // 定义二元函数的第一个参数类型为 arg1_type
return cgh_binder1st<operation>(op, arg1_type(x)); // 让一元函数绑定二元函数的第一个参数
}
#pragma endregion
#pragma region cgh_binder2nd
/* 以下配接器可以将某个 Adaptable Binary function(二元函数)转换为 Unary Function(一元函数)*/
/* 具体的做法是让一元函数绑定二元函数的第二个参数,忽略第一个参数 */
template<class operation>
class cgh_binder2nd :public unary_function < typename operation::first_argument_type,
typename operation::result_type >
{
protected:
operation op; // 对参数的操作方式
typename operation::second_argument_type value; // 二元函数的第二个参数值
public:
cgh_binder2nd(const operation& x, const typename operation::second_argument_type& y) :op(x), value(y) {}
// operation::result_type:返回值类型
typename operation::result_type operator()(const typename operation::first_argument_type& x) const
{
return op(x, value);
}
};
/* 以下函数返回一个匿名的 cgh_bind2nd 对象,这使得我们可以以函数调用的方式使用函数对象 */
template<class operation, class T>
inline cgh_binder2nd<operation> cgh_bind2nd(const operation& op, const T& x)
{
typedef typename operation::second_argument_type arg2_type; // 定义二元函数的第二个参数类型为 arg2_type
return cgh_binder2nd<operation>(op, arg2_type(x)); // 让一元函数绑定二元函数的第二个参数
}
#pragma endregion
#pragma region cgh_compose1
/*
已知两个 Adaptable Unary Functions f()、g(),cgh_unary_compose 配接器可以产生一个 h()
使得 h() = f(g(x))
*/
template<class operation1, class operation2>
class cgh_unary_compose : public unary_function < typename operation2::argument_type,
typename operation1::argument_type >
{
protected:
operation1 op1;
operation2 op2;
public:
cgh_unary_compose(const operation1& x, const operation2& y) : op1(x), op2(y) {}
typename operation1::result_type operator()(const typename operation2::argument_type& x) const
{
return op1(op2(x)); // 两个操作的合成
}
};
/* 以下函数返回一个匿名的 cgh_unary_compose 对象,这使得我们可以以函数调用的方式使用函数对象 */
template<class operation1, class operation2>
inline cgh_unary_compose<operation1, operation2> cgh_compose1(const operation1& op1, const operation2& op2)
{
return cgh_unary_compose<operation1, operation2>(op1, op2);
}
#pragma endregion
#pragma region cgh_compose2
/*
已知两个 Adaptable Unary Functions f()、g(),cgh_unary_compose 配接器可以产生一个 h()
使得 h() = f(g(x))
*/
template<class operation1, class operation2, class operation3>
class cgh_binary_compose : public unary_function < typename operation2::argument_type,
typename operation1::result_type >
{
protected:
operation1 op1;
operation2 op2;
operation3 op3;
public:
cgh_binary_compose(const operation1& x, const operation2& y, const operation3& z) : op1(x), op2(y), op3(z) {}
typename operation1::result_type operator()(const typename operation2::argument_type& x) const
{
return op1(op2(x), op3(x));
}
};
/* 以下函数返回一个匿名的 cgh_binary_compose 对象,这使得我们可以以函数调用的方式使用函数对象 */
template<class operation1, class operation2, class operation3>
inline cgh_binary_compose<operation1, operation2, operation3> cgh_compose2(const operation1& op1, const operation2& op2, const operation3& op3)
{
return cgh_binary_compose<operation1, operation2, operation3>(op1, op2, op3);
}
#pragma endregion
#pragma region cgh_pointer_to_unary_function
/
/// 以下配接器可以使我们将一般函数当成函数对象来使用,一般函数当做函数对象传递给 STL 算法,
/// 就语言层面上是可以的,就像原生指针可以当做迭代器传递给 STL 算法,但是如果不使用以下给出的配接器做一层封装
/// 那么该函数就没有配给能力,不能其他配接器接轨使用
/// 以下配接器把一元函数指针封装起来
/
template<class arg, class result>
class cgh_pointer_to_unary_function : public unary_function < arg, result >
{
protected:
result(*ptr)(arg); // 内部成员:函数指针
public:
cgh_pointer_to_unary_function(){}
explicit cgh_pointer_to_unary_function(result(*x)(arg)) : ptr(x){}
result operator()(arg x) const
{
return ptr(x); // 函数对象最终调用函数裸指针
}
};
/* 以下函数返回一个匿名的 cgh_pointer_to_unary_function 对象,这使得我们可以以函数调用的方式使用函数对象 */
template<class arg, class result>
inline cgh_pointer_to_unary_function<arg, result> cgh_ptr_fun(result(*x)(arg))
{
return cgh_pointer_to_unary_function<arg, result>(x);
}
#pragma endregion
#pragma region cgh_pointer_to_binary_function
/
/// 以下配接器可以使我们将一般函数当成函数对象来使用,一般函数当做函数对象传递给 STL 算法,
/// 就语言层面上是可以的,就像原生指针可以当做迭代器传递给 STL 算法,但是如果不使用以下给出的配接器做一层封装
/// 那么该函数就没有配给能力,不能其他配接器接轨使用
/// 以下配接器把二元函数指针封装起来
/
template<class arg1, class arg2, class result>
class cgh_pointer_to_binary_function : public binary_function < arg1, arg2, result >
{
protected:
result(*ptr)(arg1, arg2); // 内部成员:函数指针
public:
cgh_pointer_to_binary_function(){}
explicit cgh_pointer_to_binary_function(result(*x)(arg1, arg2)) : ptr(x){}
result operator()(arg1 x, arg2 y) const
{
return ptr(x, y); // 函数对象最终调用函数裸指针
}
};
/* 以下函数返回一个匿名的 cgh_pointer_to_binary_function 对象,这使得我们可以以函数调用的方式使用函数对象 */
template<class arg1, class arg2, class result>
inline cgh_pointer_to_binary_function<arg1, arg2, result> cgh_ptr_fun(result (*x)(arg1, arg2))
{
return cgh_pointer_to_binary_function<arg1, arg2, result>(x);
}
#pragma endregion
}
#endif
4.测试
测试的内容已在注释中给出。
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* chengonghao@yeah.net
*
* 功能:测试函数配接器
******************************************************************/
#include "stdafx.h"
#include <iostream>
#include <iterator>
#include "cghStl_algo.h"
#include "cgh_function_obj.h"
#include "cgh_function_adapters.h"
#include "cghVector.h"
void print(int i)
{
std::cout << i << " " ;
}
int _tmain(int argc, _TCHAR* argv[])
{
using namespace CGH;
/
/// 测试 cgh_not1 和 cgh_bind2nd
/
std::cout << "************************* 测试 cgh_not1 和 cgh_bind2nd *************************" << std::endl;
std::cout << "现有 vec1[6] = { 2, 21, 12, 7, 19, 23 }; " << std::endl;
std::cout << std::endl << "调用 count_if(vec1.begin(), vec1.end(), cgh_not1(cgh_bind2nd(less<int>(), 12)))" << std::endl;
std::cout << std::endl << "找出不小于 12 的元素个数:";
cghVector<int> vec1;
vec1.push_back(2);
vec1.push_back(21);
vec1.push_back(12);
vec1.push_back(7);
vec1.push_back(19);
vec1.push_back(23);
std::cout << count_if(vec1.begin(), vec1.end(), cgh_not1(cgh_bind2nd(less<int>(), 12))) << std::endl << std::endl << std::endl;
/
/// 测试 cgh_compose1 和 cgh_bind2nd
/
std::cout << "************************* 测试 cgh_compose1 和 cgh_bind2nd *********************" << std::endl;
std::cout << "现有 vec1[6] = { 2, 21, 12, 7, 19, 23 }; " << std::endl;
std::cout << std::endl << "对每一个元素 v 执行 ( v + 2 ) * 3,输出结果:";
std::ostream_iterator<int> outit(std::cout, " ");
CGH::transform(vec1.begin(), vec1.end(), outit, cgh_compose1(cgh_bind2nd(multiplies<int>(), 3), cgh_bind2nd(plus<int>(), 2)));
std::cout << std::endl;
std::cout << std::endl;
std::cout << std::endl;
/
/// 测试 cgh_ptr_fun
/
std::cout << "************************* 测试 cgh_ptr_fun *************************************" << std::endl;
std::cout << "现有 vec1[6] = { 2, 21, 12, 7, 19, 23 }; " << std::endl;
std::cout << std::endl << "用 cgh_ptr_fun 封装 print(函数裸指针)" << std::endl;
std::cout << std::endl << "调用 for_each(vec1.begin(), vec1.end(), cgh_ptr_fun(print));" << std::endl;
std::cout << std::endl << "依次打印 vec1:";
for_each(vec1.begin(), vec1.end(), cgh_ptr_fun(print));
std::cout << std::endl;
std::cout << std::endl;
system("pause");
return 0 ;
}
测试结果如下图所示