n classes和templates都支持接口和多态。
n 对classes而言接口是显式的,以函数签名为中心。多态则是通过virtual函数发生于运行期。
n 对template参数而言,接口是隐式的,奠基于有效表达式。多态则是通过template具体化和函数重载解析 发生于编译期。
n 声明template参数时,前缀关键字class和typename可互换。
n 使用typename标识嵌套从属类型名称;但不得在base class lists或member initialization list内以它为base class修饰符。
template<typename T>
class Derived: public Base<T>::Nested {
public:
explicit Derived (int x) : Base<T>::Nested (x)
{
typename Base<T>::Nested temp;
…
}
};
class CompanyA { public: void sendClearText(const std::string&){} void sendEncrypted(const std::string&){} };
class CompanyB { public: void sendClearText(const std::string&){} void sendEncrypted(const std::string&){} };
template<typename Company> class MsgSender { public: void sendClear(std::string&) { Company c; c.sendClearText(std::string("")); } }; |
class CompanyC { public: void sendEncrypted(const std::string& msg){} };
template<> class MsgSender<CompanyC> { public: void sendSecret(std::string&) { CompanyC c; c.sendEncrypted(""); } }; //全特化版本,没有实现sendClear
|
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>
{
public:
void sendClearMsg(std::string&)
{
sendClear(std::string("")); //当Company == CompanyZ时,这个函数不存在
}
};
Templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依联系
因非类型模板参数而造成的代码膨胀,往往可以消除,做法是以函数参数或class成员变量替换template参数。
template<typename T>
class SmartPtr{
public:
template<typename U>//member template,生成copy构造函数
SmartPtr( const SmartPtr<U>& other): heldPtr(other.get()) { } //暗示只有U*可转为T*才可通过编译
T* get( )const { return heldPtr; }
private:
T* heldPtr;
};
原始指针类型之间的转换是隐式转换,因此并未声明为explicit。
在class内声明泛化copy构造函数并不会阻止编译器生成它们自己的copy构造函数(non-template),所以如果想要控制copy构造函数的方方面面必须同时声明泛化copy构造函数和正常的copy构造函数。同理,适用于赋值操作。
template<class T>
class shared_ptr{
public:
shared_ptr ( shared_ptr const& r);
template<class Y>
shared_ptr( shared_ptr<Y> const& r);
shared_ptr& operator = (shared_ptr const& r);
template<class Y>
shared_ptr& operator = (shared_ptr<Y> const& r);
…
};
template<typename T>
class Rational
{
public:
Rational(const T& numerator = 0, const T& denominator = 1):n(numerator),d(denominator){}
T numerator()const{return n;}
T denominaotr()const{return d;}
private:
T n, d;
};
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs)
{
return Rational<T>(lhs.numerator()*rhs.numerator(), lhs.denominaotr()*rhs.denominaotr());
}
Rational<int> oneHalf(1,2);
Rational<int> result = oneHalf * 2; //编译error
operator*第一参数被声明为Rational<T>,而传给operator*的第一实参的类型是Rational<int>,所以T是int。第二参数是Rational<T>,但传入的实参是整数2。template实参推导过程中并不考虑通过构造函数而发生的隐式类型转换。因此,不会转换为Rational<int>。
解决方法:
template class内的friend声明式可以指涉某个特定函数。因此可以声明operator*是Rational<T>的一个friend函数。编译器总是能够在class Rational<T> 具现化时得知T。
template<typename T>
class Rational
{ …
friend const Rational<T> operator*(const Rational& lhs, const Rational& rhs);
};
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs){ … }
可以通过编译,但不能链接。
编译器知道我们要调用哪个函数,但那个函数只被声明于Rational内,并没有被定义出来。尽管我们意图令此class外部的operator* template提供定义式,但是行不通。
最简单的方法是:
template<typename T>
class Rational
{ …
friend const Rational<T> operator*(const Rational& lhs, const Rational& rhs)
{
return Rational<T>(lhs.numerator()*rhs.numerator(), lhs.denominaotr()*rhs.denominaotr());
}
};
参考<STL源码剖析>内的实现
TMP是编写template-based C++程序并执行编译期的过程。TMP是以C++写成,执行于C++编译器内的程序。一旦TMP程序结束执行,其输出也就是templates具现出来的若干C++源码,便会一如往常地被编译。
TMP(template metaprogramming)模板元编程可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的行为效率。
TMP是图灵完全的,足以计算任何事物。条款47中的traits就是TMP,traits引发编译期发生于类型身上的if … else计算。
示例:编译期计算阶乘!
template<unsigned n>
struct Factorial{
enum {value = n * Factorial<n-1>::value};
};
template<>
struct Fatorial<0>{
enum {value = 1};
};
std::cout<<Factorial<5>::value<<endl; //输出120