一、需求引入
我们都知道,对于一个很大的数据结构而言,采用传值的方式通常是很耗费资源的,因此对于这种数据结构,应该“传const 引用”(或者在C中传递const指针);相反,对于更小的数据结构,情况并非这么简单:通常依赖于实际的代码体系结构,总的来说小的数据结构究竟采取何种传递方式,对性能影响并不大,但是也必须小心处理
二、实现方式
面对 templates,事情变得更为棘手。我们不知道将被用来替换 template parameters 的那个type 到底有多大。此外,最终决定也不仅仅取决于大小:一个伴有「代价高昂之 copy 构造函数」的小 型结构可能会让你醒悟:以 by reference-to-const 传递惟读参数还是比较有道理的。
第一步(第一种方式):根据type的大小选择合适的传参方式
这里我们举个例子:对于不大于2个指针大小的类型,基本模板将采用传值的方式传递参数,而对于其他的类型,将采用传递const引用的方式传递参数
这就让我们联想到了在前一篇博客中提到的IfThenElse模板了
template<typename T>
class RParam {
public:
typedef typename IfThenElse<sizeof(T)<=2*sizeof(void*),
T,
T const&>::ResultT Type;
};
第二步:即使数据类型很小,也可能涉及昂贵的拷贝构造函数,因此需要编写很多全特化和局部特化
这里以Array<T>的局部特化为例:当参数是Array<T>类型的容器时,采用传递const引用的方式
template<typename T>
class RParam<Array<T> > {
public:
typedef Array<T> const& Type;
};
...
第三步(第二种方式):上面是对class类型的处理,下面我们希望在基本模板中能对非class类型以传值的方式调用,另外对于某些对性能要求比较苛刻的class类型,我们有选择的添加这些类为“传值”的方式
这让我们联想到了前面一篇博客提到的IsClassT<T> 模板 来鉴定参数是否为class types类型
template<typename T>
class RParam {
public:
typedef typename IfThenElse<IsClassT<T>::No,
T,
T const&>::ResultT Type;
}
第四步:假设我们手里有2个具体类
class MyClass1 {
public:
MyClass1 () {
}
MyClass1 (MyClass1 const&) {
std::cout << "MyClass1 copy constructor called\n";
}
};
class MyClass2 {
public:
MyClass2 () {
}
MyClass2 (MyClass2 const&) {
std::cout << "MyClass2 copy constructor called\n";
}
};
第五步:针对MyClass2 objects 将以 by value 方式传递
//全特化
template<>
class RParam<MyClass2> {
public:
typedef MyClass2 Type;
};
第六步:调用上面的模板,采用内联方式可以实现模板实参的推导
//以下函数允许参数以 by value 或 by reference 方式传递
template <typename T1, typename T2>
void foo_core (typename RParam<T1>::Type p1, typename RParam<T2>::Type p2) {
//...
}
//下面是个 wrapper,用以免除(显式)指定 template parameter的责任
template <typename T1, typename T2>
inline
void foo (T1 const & p1, T2 const & p2) {
foo_core<T1,T2>(p1,p2);
}
三、使用模板
int main() {
MyClass1
mc1;
MyClass2
mc2;
foo(mc1,mc2); // 此式效果相当于 foo_core<MyClass1,MyClass2>(mc1,mc2)
}