C++ 类方法转发技术

C++编程中,有时希望在一个类中定义的方法能够访问另一个类中的一些方法。这些方法有时被称为转发方法。本文将介绍一种称为完美转发的技术,它允许定义一个模板,适用于定义多个函数:带有引用参数的函数、带有常量引用参数的函数以及带有右值引用参数的函数。

问题演示

首先,定义一个用于封闭对象的类:

#include <iostream> #include <vector> #include <utility> using namespace std; typedef vector<double> block; class A { block x; public: A(const block& x1) : x(x1) {} A(block&& x1) : x(move(x1)) {} block add(const block& y) { cout << "use copy, non-const" << endl; size_t n = x.size(); for(size_t i = 0; i < n; i++) { x[i] += y[i]; } return x; } block add(const block& y) const { cout << "use copy, const" << endl; size_t n = x.size(); block z = y; for(size_t i = 0; i < n; i++) { z[i] += x[i]; } return z; } block add(block&& y) const { cout << "use move, const" << endl; size_t n = x.size(); for(size_t i = 0; i < n; i++) { y[i] += x[i]; } return move(y); } block get() const { return x; } };

接下来,定义类B本身:

class B { A a; public: B(const block& x1) : a(x1) {} B(block&& x1) : a(move(x1)) {} block add(const block& y) { return a.add(y); } block add(const block& y) const { return a.add(y); } block add(block&& y) { return a.add(move(y)); } block get() const { return a.get(); } };

在现实世界中,会在类B中添加一些额外的功能。以下是程序的其余部分:

void print(const string& name, const block& v) { cout << name; size_t n = v.size(); cout << "(" << n << "): "; for(size_t i = 0; i < n; i++) { cout << v[i] << (i == n-1 ? "\n" : ", "); } cout << "\n" << endl; } int main() { block z; z.push_back(10.0); z.push_back(20.0); z.push_back(30.0); B b1(z); const B b2(z); const B b3(z); block p; p.push_back(100.0); p.push_back(111.0); p.push_back(3000.0); block r1 = b1.add(p); print("p", p); print("r1", r1); block x1 = b1.get(); print("x1", x1); block r2 = b2.add(p); print("p", p); print("r2", r2); block x2 = b2.get(); print("x2", x2); block r3 = b3.add(move(p)); print("p", p); print("r3", r3); block x3 = b3.get(); print("x3", x3); return 0; }

这个程序将打印:

use copy, non-const p(3): 100, 111, 3000 r1(3): 110, 131, 3030 x1(3): 110, 131, 3030 use copy, const p(3): 100, 111, 3000 r2(3): 110, 131, 3030 x2(3): 10, 20, 30 use move, const p(0): r3(3): 110, 131, 3030 x3(3): 10, 20, 30

如所见,所有三个add函数产生相同的结果,但在幕后它们做了不同的事情:第一个改变了成员变量x,第二个没有改变任何东西,第三个改变了参数(实际上是“移动”它到结果中)。

简化成员函数别名定义

将使用的技术称为完美转发,它使能够定义一个模板,适用于定义多个函数:带有引用参数的函数、带有常量引用参数的函数以及带有右值引用参数的函数。以下是模板定义:

template block add(T&& y) { return a.add(static_cast(y)); } template block add(T&& y) const { return a.add(static_cast(y)); }

可以编写以下宏:

#define memb_alias_impl1(_a,_memb,_cv)\ template \ block _memb(T&& y) _cv {\ return _a._memb(static_cast(y));\ } #define memb_alias(_a,_memb)\ memb_alias_impl1(_a,_memb,)\ memb_alias_impl1(_a,_memb, const)

但是仍然有block类型返回。希望这个类型从_a._memb派生。为了做到这一点,让定义以下类模板

template struct memb_types { typedef _R return_type; typedef _T class_type; memb_types(_R (_T::*f)(_Arg)) {} }; template memb_types<_R, _T, _Arg> get_memb_types(_R (_T::*f)(_Arg)) { return memb_types<_R, _T, _Arg>(f); }

现在可以正确地定义memb_alias_impl1和memb_alias:

#define memb_alias_impl1(_a,_memb,_cv)\ template \ typename decltype(get_memb_types(&decltype(_a)::_memb))::return_type _memb(Args&& y) _cv {\ return _a._memb(std::forward(y));\ } #define memb_alias(_a,_memb)\ memb_alias_impl1(_a,_memb,)\ memb_alias_impl1(_a,_memb, const)

然后对于没有参数的函数(程序中只有一个),将创建memb_alias0。现在可以重写类B:

class B { A a; public: B(const block& x1) : a(x1) {} B(block&& x1) : a(move(x1)) {} memb_alias(a, add); memb_alias0(a, get); };

方法的定义更短,错误也更少。想象一下,如果不得不为几个这样的别名和几个类编写更多。

通过变长模板,可以立即处理所有参数数量。memb_types结构和get_member_types函数将被定义如下:

template struct memb_types { typedef _R return_type; typedef _T class_type; memb_types(_R (_T::*f)(_Arg...)) {} }; template memb_types<_R, _T, _Arg...> get_memb_types(_R (_T::*f)(_Arg...)) { return memb_types<_R, _T, _Arg...>(f); }

显然,必须为const成员函数定义相同的结构。之后memb_alias_impl可以被定义如下:

#define memb_alias_impl(_a,_memb,_cv)\ template \ decltype(get_memb_types(&decltype(_a)::_memb))::return_type _memb(Args&&... y) _cv {\ return _a._memb(std::forward(y)...);\ } #define memb_alias(_a,_memb)\ memb_alias_impl(_a,_memb,)\ memb_alias_impl(_a,_memb, const) memb_alias(a, add); memb_alias(a, get);
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485