41:了解隐式接口和编译器多态
1.面向对象编程世界里总是以显式接口(源码中明确可见)和运行期多态解决问题;Templates及泛型编程的世界中,与面向对象有根本上不同,在此世界中,显式接口和运行期多态仍然存在,但是重要性降低,反倒是隐式接口和编译期多态比较重要了。
template<typename T>
void doProcessing(T& w)
{
T temp(w);
temp.normalize();
temp.swap();
...
}
- w的类型T好像必须支持swap,normalize()和拷贝构造函数,这一组表达式便是T必须支持的一组隐式接口。
- 涉及w的任何函数调用得以成功的原因是template具现化,这种具现化发生在编译期,不同的template参数具现化function templates 会导致调用不同的函数,这便是所谓的编译期多态。
2.显式接口和隐式接口?
- 通常显式接口由函数的签名式(也就是函数名称、参数类型、返回类型)构成。
class Widget
{
public:
Widget();
~Widget();
virtual void normalize();
void swap(Widget& other);
...
};
- 隐式接口不基于函数签名式,而是由有效表达式组成。
template<typename T>
void doProcessing(T& w)
{
T temp(w);
temp.normalize();
temp.swap();
...
}
- 显式接口和隐式接口都在编译器完成,就像无法以一种”与类童工之显式接口矛盾“的方式来使用对象,也无法在template中使用”不支持template所要求之隐式接口“的对象。
请记住
- classes和templates都支持接口和多态。
- 对classes而言接口是显式(explicit),以函数签名为中心。多态则是通过virtual函数发生于运行期。
- 对template参数而言,接口是隐式的(implicit),奠基于有效表达式。多态则是通过template具现化和函数重载解析发生于编译期。
42:了解typename的双重意义
template<typename C>
void print2Nd(const C& container)
{
if(container.size()>=2)
{
typename C::const_iterator iter(container.begin())
....
}
}
C::const_iterator
前加上typename
,告诉C++C::const_iterator
是个类型不是局部变量。
请记住
- 声明template参数时,前缀关键字class和typename可互换。
- 请使用关键字typename标识嵌套从属类型名称;但不得在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class修饰符。
43:学习处理模板化基类内的名称
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
...
void SendClaearMsg(const MsgInfo& info)
{
sendClear(info);//调用基类函数,无法通过编译
}
....
};
原因:当编译器遇到类LoggingMsgSender:punlic定义式时,不知道它继承什么样的class。继承的MsgSender<Company>不到最后具现化无法确切知道它是什么。
解决方法:
1.在基类函数调用之前加上this->
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
...
void SendClaearMsg(const MsgInfo& info)
{
this->sendClear(info);//成立,假设sendClear将被继承
}
....
};
2.使用using声明式
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
using MsgSender<Company>::sendClear;//告诉编译器,假设sendClear位于基类内
...
void SendClaearMsg(const MsgInfo& info)
{
sendClear(info);
}
....
};
3.明白指出被调用的函数位于基类内:
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
...
void SendClaearMsg(const MsgInfo& info)
{
MsgSender<Company>::sendClear(info);//假设sendClear将被继承下来
}
....
};
如果被调用的是virtual函数,上述的明确资格修饰符会关闭”virtual绑定行为“。
请记住
- 可在derived class templates内通过“this->”指涉base class templates内的成员名称,或籍由一个明白写出的“base class资格修饰符”完成。
44:将与参数无关的代码抽离templates
请记住
- Templates生产多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。
- 因非类型模板参数而造成的代码膨胀,往往可以消除,做法是以函数参数或class成员变量替换template参数。
- 因类型参数而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述的具现类型共享代码。
45:运用成员函数模板接收所有兼容类型
template<typename T>
class SmartPtr
{
public:
template<typename U>
SmartPtr(const SmartPtr<U> &other);
...
};
泛化构造函数并未被声明为explicit,因为原始指针类型之间的转换是隐式的,无需明白写成转型动作
- member function templates(成员函数模板)的效用不限于构造函数,它们常扮演的另一个角色是支持赋值操作。例如TR1的shared_ptr
- 在类内声明泛化拷贝构造函数并不会阻止编译器会自动生成自己的拷贝构造函数(非泛化)
template<class T>
class shared_ptr
{
public:
template<class Y>
explicit shared_ptr(Y* p);
shared_ptr(shared_ptr const& r);
template<class Y>
shared_ptr(shared_ptr<Y> const& r);
template<class Y>
explicit shared_ptr(weak_ptr<Y> const& r);
template<class Y>
explicit shared_ptr(auto_ptr<Y>&r);
shared_ptr& operator=(shared_ptr const& r);
template<class Y>
shared_ptr& operator=(shared_ptr<Y> const& r);
template<class Y>
shared_ptr& operator=(auto_ptr<Y>& r);
...
};
上述所有构造函数都是explicit,唯有泛化构造函数除外,意味着从某个shared_ptr类型隐式转换至另一个shared_ptr类型是被允许的,但从某个内置指针或从其他智能指针进行隐式转换是不允许的。
请记住
- 请使用member function templates(成员函数模板)生成“可接收所有兼容类型”的函数。
- 如果你声明member templates用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数和copy assignment操作符。
46:需要类型转换时请为模板定义非成员函数
请记住
- 当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”
47:请使用traits classes表现类型信息
请记住
- Traits classes使得“类型相关信息”在编译期间可用。它们以templates和“templates特化”完成实现。
- 整合重载技术后,traits classes有可能在编译期对类型执行if…else测试。
48:认识template元编程
请记住
- Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,因而得以是实现早期错误侦测和更高的执行效率。
- TMP可被用来生产“基于政策选择组合”的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码。
46,47,48还没完全看懂,看懂后再更新吧