《MCD》仿函数学习笔记,欢迎讨论

oopig 2003-12-10 10:54:21

主要结构:
一、对一些要点,根据自己的理解做出说明
二、实做出自己的一份简单实现
三、提出一些问题

调试环境:
fedora core 1.0
Kdevelop2.1.5/g++ 3.3.2

今天先说说我理解的一些技术要点:
一、MCD仿函数的技术要点
1. 偏特化是基础,一定要掌握好
这个不用多说了,要学习mcd functor,偏特化一定要理解好,比如以下代码必须达到可以随手拈来的程度:
#include <iostream>
using namespace std;

template <typename T>
struct Refrence
{
enum { is_refrence = false };
};
template <typename T>
struct Refrence<T&>
{
enum { is_refrence = true };
};
template <typename T>
struct Refrence<const T&>
{
enum { is_refrence = true };
};

int main()
{
cout << Refrence<int>::is_refrence << endl; //output: 1
cout << Refrence<int&>::is_refrence << endl; //output: 1
cout << Refrence<const int&>::is_refrence << endl; //output: 0
return 0;
}
2.Functor的模版构造函数
要理解Functor模版构造函数的作用就是可以接受任意型别,说是任意型别,但是这里只使用于各种“函数”,包括普通函数,仿函数、成员函数等。
这一点很重要,因为MCD的仿函数能够海纳百川,通吃各种“函数”,靠的就是这一手。
再用些代码来说明:

#include <iostream>
using namespace std;

void foo1()
{ cout << "void foo1()" << endl; }
void foo2()
{ cout << "char foo2()" << endl; }

struct Function
{
template <typename Fun>
Function(const Fun &fun) //可以接收任意返回值的无显式参数的“函数”
{ fun(); }
};

int main()
{
Function fun1(static_cast<void(*)()>(foo1)); //output: void foo1()
Function fun2(static_cast<char(*)()>(foo2)); //output: char foo2()
return 0;
}
待续。。。
...全文
115 31 打赏 收藏 转发到动态 举报
写回复
用AI写文章
31 条回复
切换为时间正序
请发表友善的回复…
发表回复
yisan 2004-01-15
  • 打赏
  • 举报
回复
泛函,好称呼啊!晕死了。
今天用它的Factory居然出错了,看来得回去抱书看看,函数作为参数还没搞太明白呀。
functor--->function object,敲一敲,不然我心中也在默念仿函数了!
babysloth 2004-01-13
  • 打赏
  • 举报
回复
好啊,给大家拜个早年:-)
xiaonian_3654 2004-01-11
  • 打赏
  • 举报
回复
haha
叫做 泛函
晕死你
虫虫,最近可好?
babysloth 2004-01-11
  • 打赏
  • 举报
回复
仿函数。。这个词很好听吗。。
xueweizhong 2003-12-24
  • 打赏
  • 举报
回复
struct Function
{
template <typename Fun>
Function(const Fun &fun) //可以接收任意返回值的无显式参数的“函数”
^^^^^^^^^^^//函数左值的引用
{ fun(); }
};

int main()
{
Function fun1(foo1);
-------------#1
}

按照标准,#1应该是可以通过的。
可是现有的编译器对函数引用总是处理的不好,
而且跟构造函数搞在一起,更容易出错了。

下面是我的试的情况:
VC 7.1 error
g++ 3.3.2 error

EDG based comeaucomputing online compiler OK
Borland CBUILDER 6.0 OK



magicblue 2003-12-20
  • 打赏
  • 举报
回复
merlinran:
我查了一下C++ Standard,确实有functions of reference。
oopig 2003-12-18
  • 打赏
  • 举报
回复
利用loki functor实现思想做的一个guard,用来防止在异常情况下,资源的清理动作不能进行。guard函数可以支持一般函数,仿函数或者成员函数,但是必须是无参数或者参数缺省,可以有返回值,但是将被忽略。
//file : km_guard.h
#ifndef __KM_GUARD_H__
#define __KM_GUARD_H__
class FunctorImpl
{
public:
virtual void operator()() = 0;
virtual ~FunctorImpl() {}
};
template <typename ObjPtr, typename FunPtr>
class MemfunHandler : public FunctorImpl
{
ObjPtr _obj;
FunPtr _fun;
public:
MemfunHandler(ObjPtr obj, FunPtr fun) : _obj(obj), _fun(fun)
{}
void operator()()
{ if (_obj) (_obj->*_fun)(); }
};
template <typename Function>
class GenfunHandler : public FunctorImpl
{
Function _fun;
public:
GenfunHandler(Function fun) : _fun(fun)
{}
void operator()()
{ _fun(); }
};
class km_guard
{
bool _abort;
FunctorImpl *_impl;
public:
template <typename ObjPtr, typename FunPtr>
km_guard(ObjPtr obj, FunPtr fun)
: _abort(false), _impl(new MemfunHandler<ObjPtr, FunPtr>(obj, fun))
{}
template <typename Function>
km_guard(Function fun)
: _abort(false), _impl(new GenfunHandler<Function>(fun))
{}
~km_guard()
{
if (!_abort) (*_impl)();
delete _impl;
}
void abort()
{ _abort = true; }
};
#endif //__KM_GUARD_H__

//file main.cpp
#include "km_guard.h"
class FunObj
{
public:
void Release()
{}
};

int main()
{
FunObj fun;
km_guard guard(&fun, &FunObj::Release);
return 0; //返回前fun.Release()将被执行
}
merlinran 2003-12-18
  • 打赏
  • 举报
回复
差矣。
void(&)()就是类型,void(*)()也是类型。但只有函数指针类型可以像一般的类型那样声明变量,比如:
typedef void(FP*)();
void f() {};
FP fp = &f;
而函数的引用就不行了。但可以把函数引用作为参数传递,比如:
#include <iostream>
using namespace std;
typedef void(FunRef)();
void foo1()
{ cout << "void foo1()" << endl; }
void fun(FunRef& f) { f(); }
int main()
{
fun(foo1);
return 0;
}
用MingW的gcc版本3.2.3编译通过。

其实这一切都是为了支持函数对象之类的东西,只有在模板里,才有用处。
magicblue 2003-12-18
  • 打赏
  • 举报
回复
merlinran:
我同意你关于const方面的分析。
在void(&)()这一点上我错把它当成了一个类型,即函数的引用。实际上它代表一个void(*)(),void(&)()本身并不是一个类型,而是值,即地址
oopig 2003-12-17
  • 打赏
  • 举报
回复
二、简单实现。
说明一下,只实现了无参数,单参数和双参数,再多的参数可以通过偏特化扩充。

//---------------TypeList.h---------------------
#ifndef __TYPELIST_H__
#define __TYPELIST_H__
template <typename P1>
struct TypeList1
{
typedef P1 p1_type;
};

template <typename P1, typename P2>
struct TypeList2 : public TypeList1<P1>
{
typedef P2 p2_type;
};
#endif //__TYPELIST_H__

//---------------NullType.h---------------------
#ifndef __NULLTYPE_H__
#define __NULLTYPE_H__
struct NullType {};
#endif //__NULLTYPE_H__

//---------------FunctorImpl.h---------------------
#ifndef __FUNCTORIMPL_H__
#define __FUNCTORIMPL_H__
#include "NullType.h"
#include "TypeList.h"

template <typename Result, class TypeList>
class FunctorImpl;

template <typename Result>
class FunctorImpl<Result, NullType>
{
public:
virtual Result operator()() = 0;
virtual ~FunctorImpl() {}
};

template <typename Result, typename P1>
class FunctorImpl< Result, TypeList1<P1> >
{
public:
virtual Result operator()(P1) = 0;
virtual ~FunctorImpl() {}
};

template <typename Result, typename P1, typename P2>
class FunctorImpl< Result, TypeList2<P1, P2> >
{
public:
virtual Result operator()(P1, P2) = 0;
virtual ~FunctorImpl() {}
};

#endif //__FUNCTORIMPL_H__

//---------------FunctorHandler.h---------------------
#ifndef __FUNCTORHANDLER_H__
#define __FUNCTORHANDLER_H__
#include "FunctorImpl.h"

template <typename Result, typename TypeList , typename Fun>
class FunctorHandler;

//无参数
template <typename Result, typename Fun>
class FunctorHandler<Result, NullType, Fun> : public FunctorImpl<Result, NullType>
{
Fun _fun;
public:
FunctorHandler(const Fun &fun) : _fun(fun)
{}
Result operator()()
{ return _fun(); }
};
//单参数
template <typename Result, typename Fun, typename P1>
class FunctorHandler<Result, TypeList1<P1>, Fun> : public FunctorImpl<Result, TypeList1<P1> >
{
Fun _fun;
public:
FunctorHandler(const Fun &fun) : _fun(fun)
{}
Result operator()(P1 p1)
{ return _fun(p1); }
};
//双参数
template <typename Result, typename Fun, typename P1, typename P2>
class FunctorHandler<Result, TypeList2<P1, P2>, Fun> : public FunctorImpl<Result, TypeList2<P1, P2> >
{
Fun _fun;
public:
FunctorHandler(const Fun &fun) : _fun(fun)
{}
Result operator()(P1 p1, P2 p2)
{ return _fun(p1, p2); }
};
#endif //__FUNCTORHANDLER_H__

//---------------MemfunHandler.h---------------------
#ifndef __MEMFUNHANDLER_H__
#define __MEMFUNHANDLER_H__

#include "FunctorImpl.h"

template <typename Result, typename TypeList, typename ObjPtr, typename FunPtr>
class MemfunHandler;

//无参数
template <
typename Result,
typename ObjPtr,
typename FunPtr>
class MemfunHandler<Result, NullType, ObjPtr, FunPtr> : public FunctorImpl<Result, NullType>
{
ObjPtr _obj;
FunPtr _fun;
public:
MemfunHandler(ObjPtr obj, FunPtr fun) : _obj(obj), _fun(fun)
{}
Result operator()()
{ return (_obj->*_fun)(); }
};

//单参数
template <
typename Result,
typename ObjPtr,
typename FunPtr,
typename P1>
class MemfunHandler<Result, TypeList1<P1>, ObjPtr, FunPtr>
: public FunctorImpl<Result, TypeList1<P1> >
{
ObjPtr _obj;
FunPtr _fun;
public:
MemfunHandler(ObjPtr obj, FunPtr fun) : _obj(obj), _fun(fun)
{}
Result operator()(P1 p1)
{ return (_obj->*_fun)(p1); }
};

//双参数
template <
typename Result,
typename ObjPtr,
typename FunPtr,
typename P1,
typename P2>
class MemfunHandler<Result, TypeList2<P1, P2>, ObjPtr, FunPtr>
: public FunctorImpl<Result, TypeList2<P1, P2> >
{
ObjPtr _obj;
FunPtr _fun;
public:
MemfunHandler(ObjPtr obj, FunPtr fun) : _obj(obj), _fun(fun)
{}
Result operator()(P1 p1, P2 p2)
{ return (_obj->*_fun)(p1, p2); }
};
#endif //__MEMFUNHANDLER_H__

//---------------Functor.h---------------------
#ifndef __FUNCTOR_H__
#define __FUNCTOR_H__
#include "FunctorHandler.h"
#include "MemfunHandler.h"

template <typename Result, class TypeList>
class Functor;

//无参数
template <typename Result>
class Functor<Result, NullType>
{
FunctorImpl<Result, NullType> *_impl;
public:
template <typename Fun>
Functor(Fun fun) : _impl(new FunctorHandler<Result, NullType, Fun>(fun))
{}
template <typename ObjPtr, typename FunPtr>
Functor(ObjPtr obj, FunPtr fun)
: _impl(new MemfunHandler<Result, NullType, ObjPtr, FunPtr>(obj, fun))
{}
Result operator()()
{ return (*_impl)(); }
};
//单参数
template <typename Result, typename P1>
class Functor<Result, TypeList1<P1> >
{
FunctorImpl<Result, TypeList1<P1> > *_impl;
public:
template <typename Fun>
Functor(Fun fun) : _impl(new FunctorHandler<Result, TypeList1<P1>, Fun>(fun))
{}
template <typename ObjPtr, typename FunPtr>
Functor(ObjPtr obj, FunPtr fun)
: _impl(new MemfunHandler<Result, TypeList1<P1>, ObjPtr, FunPtr>(obj, fun))
{}
Result operator()(P1 p1)
{ return (*_impl)(p1); }
};

//双参数
template <typename Result, typename P1, typename P2>
class Functor<Result, TypeList2<P1, P2> >
{
FunctorImpl<Result, TypeList2<P1, P2> > *_impl;
public:
template <typename Fun>
Functor(Fun fun) : _impl(new FunctorHandler<Result, TypeList2<P1, P2>, Fun>(fun))
{}
template <typename ObjPtr, typename FunPtr>
Functor(ObjPtr obj, FunPtr fun)
: _impl(new MemfunHandler<Result, TypeList2<P1, P2>, ObjPtr, FunPtr>(obj, fun))
{}
Result operator()(P1 p1, P2 p2)
{ return (*_impl)(p1, p2); }
};
#endif //__FUNCTOR_H__

//---------------main.cpp---------------------
#include <iostream>
#include "Functor.h"
using namespace std;

void foo(int)
{
cout << "void foo(int)" << endl;
}

class Function
{
public:
int operator()()
{
cout << "int Function::foo()" << endl;
return 0;
}
const char *foo(int, int)
{
const char *msg = "char *foo(int, int)";
return msg;
}
};

int main()
{
Functor<void, TypeList1<int> > cmd1(foo);
cmd1(10);

Function fun;
Functor<int, NullType> cmd2(fun);
cmd2();

Functor<const char*, TypeList2<int, int> > cmd3(&fun, &Function::foo);
cout << cmd3(10, 100) << endl;

system("PAUSE");
return 0;
}
oopig 2003-12-17
  • 打赏
  • 举报
回复
6.向成员函数进军。
到目前为止,虽然还没有完整的代码实现(稍后给出),已经大致知道loki仿函数对于原生函数和函数对象支持的实现原理了。但是对于类成员函数呢?其实只要稍微变通一下,另外实现一个FunctorImpl接口,然后增加一个Functor的模板构造函数就可以了。我们这里把这个FunctorImpl的实现命名为MemfunHandler:
template <typename ObjPtr, tyename FunPtr>
class MemfunHandler : public FunctorImpl
{
ObjPtr _obj;
FunPtr _fun;
public:
FunctorHandler(ObjPtr obj, FunPtr fun) : _obj(obj), _fun(fun)
{}
...
};
e <typename Result, typename TypeList>
class Functor
{
FunctorImpl *_impl;
public:
template <typename Fun>
Fucntor(Fun fun) : _impl(new FunctorHandler<Fun>(fun))
{}
template <typename ObjPtr, typename FunPtr>
Functor(ObjPtr obj, FunPtr fun) : _impl(new MemfunHanlder(obj, fun))
{}
};
至于这里为什么使用ObjPtr而不是Object,《MCD》中有很好的解释:为了兼容“类指针”,比如iterator这样的数据类型。
merlinran 2003-12-17
  • 打赏
  • 举报
回复
如果把构造函数改为接收非常量引用,则G++顺利通过,Digital Mars警告:
non-const reference initialized to temporary
警告得有道理。
而BCC就不听话了:
Error E2034 main.cpp 20: Cannot convert 'void()' to 'void (*)()' in function main()
Error E2342 main.cpp 20: Type mismatch in parameter 'fun' (wanted 'void (&)()', got 'void()') in function main()
两个错误信息自相矛盾。
到底哪个正确,吾也不知。

PS:用三个编译器编译同一段代码,会得到很多有趣的结果。以前用VC、BCC、GCC,现在VC换成了Digital Mars,看起来它要更遵循标准一点,而且可以免费在www.digitalmars.com下载。
merlinran 2003-12-17
  • 打赏
  • 举报
回复
引用
要用static_cast的原因是在进行模版参数推导时,Fun和foo的类型为void()(),Fun&的类型为void(&)(),这个类型是错误的,不可能有对函数本身的引用,所以推导失败。

void(&)()并不是一个错误的类型,既然可以有void(*)(),为何不能有void(&)()?类型系统不需要对函数格外处理。我用BCC和Digital Mars C++编译,都成功。而g++的输出是:
main.cpp:20: no matching function for call to `Function::Function(void (&)())'
main.cpp:10: candidates are: Function::Function(const Function&)
main.cpp:21: no matching function for call to `Function::Function(char (&)())'
main.cpp:10: candidates are: Function::Function(const Function&)
看来,它是无法把函数作为常量引用传递。
不知哪位有心去查一查标准,到底对此如何规定的?
step_by_step 2003-12-15
  • 打赏
  • 举报
回复
不错,continue!
magicblue 2003-12-14
  • 打赏
  • 举报
回复
要用static_cast的原因是在进行模版参数推导时,Fun和foo的类型为void()(),Fun&的类型为void(&)(),这个类型是错误的,不可能有对函数本身的引用,所以推导失败。使用static_cast将foo的类型转换为void(*)(),即生成临时函数指针。Fun的类型为void(*)(),Fun&的类型为 & void(*)()
可以看出Fun&中的&有点多余。因为传递的只是类型,所以引用的作用反而会添乱,而且在可能会产生&&的问题。特别是模版参数的多层推导,尽量去掉引用可以避免这种问题的产生
oopig 2003-12-13
  • 打赏
  • 举报
回复
5.loki functor的解决之道
现在我们可以用4.中的思路来尝试着弥补模板构造函数的缺陷了。为了方便起见,我们先用TypeList来表示不同数目不同类型的参数类型(下面我们会看到完全可以用一个typename来约定所有的参数类型),用Result表示函数的返回值。大致是这个样子:
template <typename Result, typename TypeList>
class Functor
{
FunctorImpl *_impl;
public:
template <typename Fun>
Fucntor(Fun fun) : _fun(new FunctorHandler<Fun>(fun))
{}
};
其中FunctorImpl<Result, TypeList> *_impl是一个抽象类指针,所以可以在形式上与Fun无关,而实际上却是指向了了解Fun型别的具体对象;FunctorHandler<Fun>就是这个具体对象的类型。因此我们可以想象FunctorHandler大致是这样的:
template <typename Fun>
class FunctorHandler : public FunctorImpl
{
Fun _fun;
public:
FunctorHandler(Fun fun) : _fun(fun)
{}
};
现在还有个类型问题要解决,就是要用一个typename来表示所有的参数类型。可以这样子:
//表示没有参数
struct Empty {};
//一个参数
template <typename P1>
struct TypeList1
{
typedef P1 p1_type;
};
//两个参数
template <typename P1, typename P2>
struct TypeList2 : public TypeList1<P1>
{
typedef P2 p2_type;
};
//三个参数
template <typename P1, typename P2, typename P3>
struct TypeList3 : public TypeList2<P1, P2>
{
typedef P3 p3_type;
};
以下依次类推。
dft2000 2003-12-13
  • 打赏
  • 举报
回复
#include <iostream>
using namespace std;

void foo1() { cout << "void foo1()" << endl; }
void foo2() { cout << "char foo2()" << endl; }

struct Function
{
template <typename Fun>
Function(Fun fun) //注意这里的变化
{ fun(); }
};

int main()
{
Function fun1(foo1); //output: void foo1()
Function fun2(foo2); //output: char foo2()
return 0;
}
在这里其实你有没有考虑类型匹配的问题,实际上传入应该是一个对象,但是你在这里传入的是一个函数指针。所以前面使用const&是不能通过编译的。需要强制转换的原因 也在这里。
如果把 void foo1() { cout << "void foo1()" << endl; }
void foo2() { cout << "char foo2()" << endl; }实现为类的话。
class FoolCalsss
{
public:
void operator() () { cout << "void foo1()" << endl;}
};
class Foo2Calsss
{
public:
void operator() () { cout << "char foo2()" << endl;}
}
引用时 实现:
int main()
{
Function fun1(FoolCalsss foo1); //output: void foo1()
Function fun2(Foo2Calsss foo2); //output: char foo2()
return 0;
}

这是你用const &就应该可以的。
但是有些时候 为了考虑安全性,有些型别是要求传值的。
dft2000 2003-12-13
  • 打赏
  • 举报
回复
各有千秋了 。实现中由于<class T>的型别之差异,所以每一个operator() (T t) const {return -t;}的实现都是有差别的!
dft2000 2003-12-13
  • 打赏
  • 举报
回复
class Negate
{
public:
int operator() (int n) { return -n;}
};
普通函数对象
class GenericNegate
{
public:
template <class T> T operator() (T t) const {return -t;}
};
任意型别函数对象
hpho 2003-12-11
  • 打赏
  • 举报
回复
仿函数应该是指重载类的operator()()吧.
Funcion感觉只是一个载体.
加载更多回复(11)

24,854

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 工具平台和程序库
社区管理员
  • 工具平台和程序库社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧