在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);