一套用于创建和操作函子的有用函子






3.20/5 (7投票s)
2003年3月4日
5分钟阅读

63042

329
从函数、成员函数和操作函子创建函子。
引言
函子(函数对象)在标准模板库 (STL) 中扮演着重要角色。我创建了几个函子和支持函数来操作函子,以便能够轻松地生成与现有函子略有不同的新函子,而无需重写现有函子。这符合代码重用的原则。
限制
以下函子使用了模板类的偏特化。因此,它不能使用 Visual C++ 编译,因为它不支持偏特化。欢迎任何人将代码移植到 Visual C++。您只需要更改所有函子的名称,使它们变得唯一,例如:FunctionToFunctor1Arg
、FunctionToFunctor2Arg
等。
转换为函子
要操作函子,您首先需要有函子。然而,遗留库中的许多有用代码不是函子,它们大多是函数,有些可能是成员函数。即使存在函子,它们也可能不符合 STL 标准(即它们没有 typedef
它们的返回类型和参数)。将它们转换为符合 STL 标准的函子并不总是可能的,因为可能无法获得源代码,或者手动转换非常繁琐。
为了方便将这些函数转换为符合 STL 标准的函子,创建了四个具有偏特化的函子。此外,还提供了一个重载的支持函数来简化这四个函子的使用。这些函子是:FunctionToFunctor
、MemberFunctionToFunctor
、ConstMemberFunctionToFunctor
和 FunctorToFunctor
。支持函数是 to_functor
。这些函子能够将多达十个参数的函数、成员函数或函子转换为相应的符合 STL 标准的函子。支持的参数数量上限为十个,因为很少有函数的参数超过十个。
用法
FunctionToFunctor
的使用方式是 FunctionToFunctor<Arg1T,Arg2T,....,ArgNT,RetT>
,其中 Arg1T..ArgNT
(N
<=10) 是函数各个参数的类型,RetT
是函数的返回类型。FunctionToFunctor
的构造函数需要函数的名称。
MemberFunctionToFunctor
的使用方式是 MemberFunctionToFunctor
<ObjectT,Arg1T,Arg2T,....,ArgN,RetT>
,其中 ObjectT
是包含成员函数的对象的类型,Arg1T..ArgNT
(N
<=10) 是函数各个参数的类型,RetT
是函数的返回类型。MemberFunctionToFunctor
的构造函数需要一个对象实例和成员函数的名称。
ConstMemberFunctionToFunctor
的使用方式是 ConstMemberFunctionToFunctor <ObjectT,Arg1T,Arg2T,....,ArgN,RetT>
,其中 ObjectT
是包含成员函数的对象的类型,Arg1T..ArgNT
(N
<=10) 是函数各个参数的类型,RetT
是函数的返回类型。ConstMemberFunctionToFunctor
的构造函数需要一个对象实例和 const 成员函数的名称。
FunctorToFunctor
的使用方式是 FunctorToFunctor <FunctorT,Arg1T,Arg2T,....,ArgNT,RetT>
,其中 FunctorT
是函子的类型,Arg1T..ArgNT
(N
<=10) 是函数各个参数的类型,RetT
是函数的返回类型。FunctorToFunctor
的构造函数需要一个函子实例。
to_functor
的使用方式是:对于函数,使用 to_functor(function_name)
;对于成员函数,使用 to_functor(Object_instance, Object::function_name)
;对于函子,使用 to_functor<Arg1T,Arg2T,...., ArgNT,RetT>(functor_instance)
。
示例
int Square(int x) {return x*x;} FunctionToFunctor<int,int> square(Square); cout << square(3) << endl; // Output is 9 void Cube(const double& x, double& y) {y = x*x*x;} FunctionToFunctor<const double&,double&,void> cube(Cube); double z = 0.0; cube(2,z); cout << z << endl; // Output is 8 struct Test { bool IsPositive(double x) {return x > 0;} bool IsEven(int x) const {return !(x%2);} }; MemberFunctionToFunctor<Test,double,bool> isPos(Test(), Test::IsPositive); cout << isPos(3.2) << endl; // Output is 1 ConstMemberFunctionToFunctor<Test,int,bool> isEven(Test(), Test::IsEven); cout << isEven(4) << endl; // Output is 1 struct IsNegative { bool operator()(int x) {return x < 0;} }; FunctorToFunctor<IsNegative,int,bool> isNeg(IsNegative()); cout << isNeg(-2) << endl; // Output is 1 cout << to_functor(Square)(2) << endl; // Output is 4 cout << to_functor(Test(), Test::IsPositive)(2.2) << endl; // Output is 1 cout << to_functor<int,bool>(IsNegative())(4) << endl; // Output is 0
修改函子的结果
有时能够操作函子的结果是有用的。假设您想创建一个函子,它与现有函子相似,但其结果总是现有函子的 5 倍。为此,我们创建了 AlterFunctorResult
。
用法
AlterFunctorResult
的使用方式是 AlterFunctorResult<FunctorT,Arg1T,...ArgNT,RetT,BinaryOperationT>
,其中 FunctorT
、ArgNT
的定义与上面相同,BinaryOperationT
是一个执行二元运算的函子,它接收两个输入并输出一个单一的输出。例如 STL 中的 plus<>
、minus<>
、multiplies<>
和 divides<>
。AlterFunctorResult
的构造函数需要一个要修改的函子的实例,用于修改现有函子输出的输入,以及一个二元运算函子的实例。
示例
struct Cube { double operator()(double x) {return x*x;} }; AlterFunctorResult<Cube,double,double,multiplies<double> > cubeMultFive(Cube(), 5.0, multiplies<double>()); cout << cubeMultFive(3) << endl; // Output is 135
为函子的参数设置默认值
最后一套函子执行的操作与 STL 库的 bind1st
和 bind2nd
函数相同。也就是说,它允许函子的一个参数具有默认值。因此,在调用函子时,可以省略该参数。STL 库的限制是它只支持最多 2 个参数的函子。这套函子旨在补充 STL 库,支持最多 10 个参数的函子。通过支持最多 10 个参数,这也意味着程序员可以选择绑定从第一个参数到第十个参数,而不仅仅是前两个参数。
用法
这套函子的通用格式是 BindFunctor???
,其中 ???
是 1st、2nd、3rd、4th、5th、6th、7th、8th、9th 或 10th。所有这十套函子都具有相同的语法。例如,要对一个有 4 个参数的函子使用 BindFunctor3rd
,您将使用 BindFunctor3rd<FunctorT,Arg1T,Arg2T,Arg3T,Arg4T,RetT>
,其构造函数需要函子的实例和第三个参数的默认值。
示例
struct BoxVolume { int operator()(int length, int breadth, int height) {return length * breadth * height;} }; //Set breadth to be a constant 3 BindFunctor2nd<BoxVolume,int,int,int,int> boxvol(BoxVolume(), 3); cout << boxvol(2,5) << endl; // Output is 30 cout << boxvol(1,3) << endl; // Output is 9
摘要
这些函子集的有用性取决于程序员的想象力。我相信它将对泛型编程有用,因为它能够将遗留的 C 函数转换为函子,然后可以将这些函子以多种方式进行操作,然后再馈送到另一个函数。这使得在不重写现有函数的情况下创建新函数成为可能。
请告知我代码中的任何错误,以便我进行纠正。检查代码中的细微编码错误是一件不容易的事,因为代码相当重复且枯燥乏味。我也很乐意了解对代码的任何期望的改进。