了解编译期多态
多态分为编译期多态和运行期多态,编译期多态类似于决定哪个重载函数被调用,运行期多态决定哪个virtual函数被绑定。
//定义两家不同类型的company
class CompanyA {
pubic:
void sendCleartext(const std::string& msg);
};
class CompanyB {
pubic:
void sendCleartext(const std::string& msg);
};
class MsgInfo {...}; //定义消息类型
template
class MsgSender {
public:
...
void sendClear(MsgInfo& info){
std::string msg;
company c;
c.sendCleartext(msg);
}
};
MsgSender可以实现发送消息的作用。假设如果后面增加需求,比如在发送信息时增加日志信息,那么可用继承类增加这样的生产力:
templateclass LogMsgSender: public MsgSender { public: ... void sendClearLog(MsgInfo& info) { //增加log信息 sendClear(info); } };
这里继承类的接口sendClearLog与基类接口名sendClear不同,可以避免基类接口被遮掩。但是上述代码还是无法编译,显示找不到sendClear。原因是因为编译器在处理LogMsgSender时,在LogMsgSender被具现化之前是无法确认其继承的是什么类型的Company。而不知道Company是什么,就无法知道MsgSender
比如,因为模板可能被特化,而特化后的版本可能不提供和普通模板相同的接口,参考下例class:
class CompanyZ {
//不提供任何接口
};
template<>
class MsgSender {
public:
...
void sendClear(MsgInfo& info){...} //这里使用单独的处理逻辑
};
上面MsgSender针对CompanyZ全特化了,重新回到前面的LogMsgSender,如果基类被指定为MsgSender
如何解决呢,对于继承类模板的子类,如果需要使用父类的非虚函数,有两种办法:
templateclass LogMsgSender: public MsgSender { public: ... void sendClearLog(MsgInfo& info){ //增加log信息 this->sendClear(); //假设sendClear将被继承 } };
第二种是使用using声明式:
using base::sendClear;
显式告诉编译器,假设sendClear位于base class内。using还有其他用途,比如防止基类接口名称被继承类接口名称遮盖。
这种这做法可以通过编译,但是如果对特化模板对象调用SendClearMsg,而特化模板没提供SendClear接口,还是不会通过编译。
将与参数无关的代码抽离templates
template可以避免相同功能的代码重复,但是在编写模版函数时需要将重复代码抽离出来,防止代码膨胀。
参考如下例子:
templateclass SquareMatrix{ public: ... void invert(); };
这个模板接受一个类型参数T,以及一个类型为size_t的非类型参数。
考虑如下代码:
SquareMatrixsm1; sm1.invert(); SquareMatrix sm2; sm2.invert();
这里仅仅因为size不同,就具现化两份invert函数,这就是template引起代码膨胀的例子。我们可以考虑为它们建立一个带数值参数的函数,从而不重复代码:
templateclass SquareMatrixbase{ public: void invert(std::size_t Size); }; template class SquareMatrix: private SquareMatrixbase{ private: using SquareMatrixbase ::invert; public: //调用base class的invert void invert(std::size_t size) {this->invert(n);} };
这里SquareMatrixbase只对矩阵元素对象的类型进行参数化,不对矩阵的尺寸进行参数化。
图灵完全
TMP模版元编程是图灵完全的,意味着可以用来声明变量、执行循环、编写及调用函数。
如使用模板可以在编译期计算阶乘:
templatestruct Factional{ enum {value = n * Factional ::value}; }; template <> //特化 struct Factional<0>{ enum {value = 1}; };
这里模版类采用“递归模版具现化”来取代循环,每个具现体都有一份自己的value。调用Factional
typename的双重意义
声明template参数时,前缀关键字class与typename可互换;
typename可标识从属类型名称,如下例:
templatevoid func(const C& container) { ... //在行首加上typename,标识a是C的一个内部类型,而不是变量。 typename C::a * x; ... }
还有一个模版的全特化与偏特化,后续再写。



