深入理解C++11(二十)
可变参数模板消除重复代码
可变参数模板的一个特性是参数包中的模板参数可以是任意数量和任意类型的,因此,可以以一种更加泛化的方式去处理一些问题,比如一个泛型的打印函数,在C++11之前,要写一个通用的打印函数可能不得不像代码清单3-3这样写。
template<typename T>
void Print(T t)
{
cout<<t<<endl;
}
template<typename T1, typename T2>
void Print(T1 t1, T2 t2)
{
cout<<t1<<t2<<endl;
}
template<typename T1, typename T2, typename T3>
void Print(T1 t1, T2 t2, T3 t3)
{
cout<<t1<<t2<<t3<<endl;
}
template<typename T1, typename T2, typename T3, typename T4>
void Print(T1 t1, T2 t2, T3 t3, T4 t4)
{
cout<<t1<<t2<<t3<<t4<<endl;
}
template<typename T1, typename T2, typename T3, typename T4, typename T5>
void Print(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
cout<<t1<<t2<<t3<<t4<<t5<<endl;
}
可以看到这个Print函数最多只能支持5个模板参数的打印,而且很多重复的模板定义,当需要打印更多的参数时不得不再增加模板定义,当然可以通过一些宏手段消除这种重复,但这又带来难于调试以及代码可读性变差的问题。通过可变参数模板可以完全消除这种重复,代码简洁而优雅,如下:
template<typename T>
void Print(T t)
{
cout<<t<<endl;
}
template<typename T, typename... Args>
void Print(T t, Args... args)
{
cout<<t;
Print(args...);
}
在C++11之前写一个对象创建的工厂函数,也需要写很多重复的模板定义,如代码清单3-4所示
template<typename T>
T* Instance()
{
return new T();
}
template<typename T, typename T0>
T* Instance(T0 arg0)
{
return new T(arg0);
}
template<typename T, typename T0, typename T1>
T* Instance(T0 arg0, T1 arg1)
{
return new T(arg0, arg1);
}
template<typename T, typename T0, typename T1, typename T2>
T* Instance(T0 arg0, T1 arg1, T2 arg2)
{
return new T(arg0, arg1, arg2);
}
template<typename T, typename T0, typename T1, typename T2, typename T3>
T* Instance(T0 arg0, T1 arg1, T2 arg2, T3 arg3)
{
return new T(arg0, arg1, arg2, arg3);
}
template<typename T, typename T0, typename T1, typename T2, typename T3, typename
T4>
T* Instance(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
return new T(arg0, arg1, arg2, arg3, arg4);
}
struct A
{
A(int){}
};
struct B
{
B(int,double){}
};
A* pa = Instance<A>(1);
B* pb = Instance<B>(1,2);
可以看到一样的问题,存在大量的重复的模板定义以及限定的模板参数。用可变模板参数可以消除重复,同时去掉参数个数的限制,代码很简洁,通过可变参数模版优化后的工厂函数如下:
template<typename... Args>
T* Instance(Args... args)
{
return new T(args...);
}
A* pa = Instance<A>(1);
B* pb = Instance<B>(1,2);
在上面的实现代码T*Instance(Args…args)中,Args是值拷贝的,存在性能损耗,可以通过完美转发来消除损耗(关于完美转发,读者可以参考第2章的内容介绍),代码如下:
template<typename... Args>
T* Instance(Args&&... args)
{
return new T(std::forward<Args >(args)...);
}