介绍
在 C++ 中通过一个全局函数来绑定到对象的成员函数是很有用的,这个特性也存在于其他语言中,例如 C#的委派。在 C++ 中相当于成员函数指针,但是 并没有提供相应的特性。在这篇文章中,我想提出一个简单的 C++ 委派的实现,是用 C++ 成员函数指针和 C++11 的可变模板(variadic templates),目前这套实现方法仅支持 GNU C++ 4.7.0,在 Windows 下可使用 MinGW。
背景
在我的方法中奖提供一个create_delegate函数,可通过下面两种方法来调用:
1
2
|
create_delegate(&object, &member_function) create_delegate(&function) |
第一种方法创建一个对象并提供一个operator()成员函数,第二个方法生成一个函数指针,两种方法都兼容 type function<...>.
示例程序
首先我们定义一个包含多个方法的类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
class A { int i; public : A( int k):i(k) {} auto get() const -> int { return i;} auto set( int v)-> void { i = v;} auto inc( int g)-> int & { i+=g; return i;} auto incp( int & g)-> int & { g+=i; return g;} auto f5 ( int a1, int a2, int a3, int a4, int a5) const -> int { return i+a1+a2+a3+a4+a5; } auto set_sum4( int &k, int a1, int a2, int a3, int a4)-> void { i+=a1+a2+a3+a4; k = i; } auto f8 ( int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) const -> int { return i+a1+a2+a3+a4+a5+a6+a7+a8; } static auto sqr( double x)-> double { return x*x; } }; |
请注意你并不需要一定使用 C++ 的 auto 函数语法,你也可以使用传统的方法,然后我们使用下面方法创建一个类:
1
|
A a(11); |
接下来我们创建委派:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
auto set1 = create_delegate(&a,&A::set); auto inc = create_delegate(&a,&A::inc); std::function< int ( int &)> incp = create_delegate(&a,&A::incp); auto af5 = create_delegate(&a,&A::f5); auto set_sum4= create_delegate(&a,&A::set_sum4); auto af8 = create_delegate(&a,&A::f8); auto sqr = create_delegate(&A::sqr); // static function </int(int&)> set1(25); int x = 5; int k = inc(x); k = incp(x); std::cout << "a.get():" << a.get() << std::endl; std::cout << "k: " << k << std::endl; std::cout << "x: " << x << std::endl; std::cout << "af5(1,2,3,4,5): " << af5(1,2,3,4,5) << std::endl; set_sum4(x,1,2,3,20); std::cout << "after set_sum4(x,1,2,3,20)" << std::endl; std::cout << "a.get(): " << a.get() << std::endl; std::cout << "x: " << x << std::endl; std::cout << "af8(1,2,3,4,5,6,7,8): " << af8(1,2,3,4,5,6,7,8) << std::endl; std::cout << "sqr(2.1): " << sqr(2.1) << std::endl; |
执行上述程序的打印结果如下:
1
2
3
4
5
6
7
8
9
|
a.get():30 k: 35 x: 35 af5(1,2,3,4,5): 45 after set_sum4(x,1,2,3,20) a.get(): 56 x: 56 af8(1,2,3,4,5,6,7,8): 92 sqr(2.1): 4.41 |
关键点
对于一个不是 volatile 和 const 的简单函数而言,实现是非常简单的,我们只需要创建一个类保存两个指针,一个是对象,另外一个是成员函数:
1
2
3
4
5
6
7
8
9
10
11
|
template < class T, class R, class ... P> struct _mem_delegate { T* m_t; R (T::*m_f)(P ...); _mem_delegate(T* t, R (T::*f)(P ...) ):m_t(t),m_f(f) {} R operator()(P ... p) { return (m_t->*m_f)(p ...); } }; |
可变模板 variadic template 允许定义任意个数和类型参数的operator()函数,而create_function 实现只需简单返回该类的对象:
1
2
3
4
5
6
|
template < class T, class R, class ... P> _mem_delegate<T,R,P ...> create_delegate(T* t, R (T::*f)(P ...)) { _mem_delegate<T,R,P ...> d(t,f); return d; } |
实际中,我们需要另外的三个实现用于覆盖 const、volatile 和 const volatile 三种成员函数,这也是为什么传统使用 #define 宏很便捷的原因,让你无需重写代码段,下面是完整的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
template < class F> F* create_delegate(F* f) { return f; } #define _MEM_DELEGATES(_Q,_NAME)\ template < class T, class R, class ... P>\ struct _mem_delegate ## _NAME\ {\ T* m_t;\ R (T::*m_f)(P ...) _Q;\ _mem_delegate ## _NAME(T* t, R (T::*f)(P ...) _Q):m_t(t),m_f(f) {}\ R operator()(P ... p) _Q\ {\ return (m_t->*m_f)(p ...);\ }\ };\ \ template < class T, class R, class ... P>\ _mem_delegate ## _NAME<T,R,P ...> create_delegate(T* t, R (T::*f)(P ...) _Q)\ {\ _mem_delegate ##_NAME<T,R,P ...> d(t,f);\ return d;\ } _MEM_DELEGATES(,Z) _MEM_DELEGATES( const ,X) _MEM_DELEGATES( volatile ,Y) _MEM_DELEGATES( const volatile ,W) |