Effective C++系列文章:
Effective C++ 1自己习惯C++ 条款01-条款04
Effective C++ 2 构造/析构/赋值运算 条款5-条款12
Effective C++ 3 资源管理 条款13-条款17
Effective C++ 4 设计与声明 条款18-条款25
Effective C++ 5 实现 条款26-条款31
Effective C++ 6 继承与面向对象设计 条款32-条款40
Effective C++ 7 模板与泛型编程 条款41-条款52
Effective C++ 8 杂项讨论 条款53-条款55
条款41 隐式接口和编译器多态
模板要求w有size()、swap()等接口,这是隐式要求。
条款42 typename双重意义
声明template参数时,不论关键字class或typename,用法和作用没有不同;
有的只能用typename
template<class C>
void print2nd(const C container){
if(container.size() >= 2){
C::const_iterator iter(container.begin());//编译出错,解析 C::const_iterator时出问题
++iter;
int value = *iter;
cout<<value;
}
}
从属名称(dependent names): template中出现的名称相依与某个template参数
嵌套从属名称(nested dependent name):从属名称在class中呈嵌套状 例如C::const_iterator(它还是个嵌套从属类型名称,也就是个嵌套从属名称并且指某个类型。
非从属名称(non-dependent name):不依赖任何template参数的名称,例如int
嵌套从属名称可能导致解析困难,例如使用
template<typename C>
void print2nd(const C& container){
C::const_iterator *x;
}
解析时不知道C::const_iterator是个类型名称还是个变量名称(就是两个变量相乘)
缺省情况下嵌套从属名称不是类型
template
void print2nd(const C container){
if(container.size() >= 2){
C::const_iterator iter(container.begin());
…
}
}
编译出错,解析 C::const_iterator被默认解析为一个变量名。
一般性规则:任何时候想要在template中指涉(refer to)一个嵌套从属类型名称,就必须在紧邻它的前一个位置放上关键字typename,
typename只被用来验明嵌套从属类型名称,其他名称不该有它存在
例外~~~!!!!!!!!!
总结:
条款43 处理模板化基类内的名称
class CompanyA{
public:
void sendCleartext(const string& msg);
void sendEncrypted(const string& msg);
};
class MsgInfo{};
template<typename Company>
class MsgSender{
public:
void sendClear(const MsgInfo &info){
string msg;
Company c;
c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& info);
};
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
void sendClearMsg(const MsgInfo& info){
sendClear(info);;//编译出错
}
};
直接使用SendClear(info)编译出错
class CompanyZ{
public:
void sendEncrypted(const string& ms);
}
假设有个class CompanyZ坚持使用加密通讯,这意味着一般的MsgSender template不适合,因为那个template提供了一个sendClear函数,这对CompanyZ不合理,所以针对CompanyZ进行特化:
template<>
class MsgSender<CompanyZ>{
//一个全特化的MsgSender,它和一般template相同,差别在于它删掉了sendClear
public:
void sendSecret(const MsgInfo& info);
}
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
void sendClearMsg(const MsgInfo& info){
sendClear(info);;//如果Company==CompanyZ,这个函数不存在
}
};
解决办法:
- 在base class函数调用动作之前加上“this->”
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
void sendClearMsg(const MsgInfo& info){
this->sendClear(info);//OK,假设sendClear被继承下来
}
};
- 使用using声明式
条款33说:使用using表达式将被掩盖的基类名称代入一个继承类作用域中,但是这里的情况不是基类名称被继承类覆盖,而是编译器不进入基类作用域内查找,于是我们通过using告诉它,请它这么做。
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
using MsgSender<Company>:sendClear;//告诉编译器,请它假设sendClear位于base class内
void sendClearMsg(const MsgInfo& info){
sendClear(info);//OK,假设sendClear被继承下来
}
};
- 明确指出被调用的函数位于基类中
但这不是一个好方法(最坏的一种解决办法),因为如果被调用的是virtual函数,上述调用规则会关闭“virtual绑定行为”
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
void sendClearMsg(const MsgInfo& info){
MsgSender<Company>::sendClear(info);//OK,假设sendClear被继承下来
}
};
条款44 将与参数无关的代码抽离templates
好好学模板 之后再来看这些