1、The initial motivation for C++ templates was straightforward: to make it possible to create type-safe containers like vector, list, and map. Ultimately, it was discovered that the C++ template mechanism is itself Turing-complete: it can be used to compute any computable value. That led to template metaprogramming: the creation of programs that execute inside C++ compilers and that stop running when compilation is complete.

This chapter won't make you an expert template programmer, but it will make you a better one. It will also give you information you need to expand your template-programming boundaries as far as you desire.

<Item 41> Understand implicit interfaces and compile-time polymorphism

2、The world of object-oriented programming revolves around explicit interfaces and runtime polymorphism.The world of templates and generic programming is fundamentally different. In that world, explicit interfaces and runtime polymorphism continue to exist, but they're less important. Instead, implicit interfaces and compile-time polymorphism move to the fore.

3、如下代码意味着可以描述w如下两点

  • The interface that w must support is determined by the operations performed on w in the template. In this example, it appears that w's type (T) must support the size, normalize, and swap member functions; copy construction (to create temp); and comparison for inequality (for comparison with someNastyWidget). We'll soon see that this isn't quite accurate, but it's true enough for now. What's important is that the set of expressions that must be valid in order for the template to compile is the implicit interface that T must support.

  • The calls to functions involving w such as operator> and operator!= may involve instantiating templates to make these calls succeed. Such instantiation occurs during compilation. Because instantiating function templates with different template parameters leads to different functions being called, this is known as compile-time polymorphism.

template<typename T>
void doProcessing(T& w)
{
if (w.size() > && w != someNastyWidget) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}

4、An explicit interface typically consists of function signatures, i.e., function names, parameter types, return types, etc.An implicit interface is quite different. It is not based on function signatures. Rather, it consists of valid expressions. Look again at the conditional at the beginning of the doProcessing template:

template<typename T>
void doProcessing(T& w)
{
if (w.size() > && w != someNastyWidget) {
...
}

The implicit interface for T (w's type) appears to have these constraints:

  • It must offer a member function named size that returns an integral value.

  • It must support an operator!= function that compares two objects of type T. (Here, we assume that someNastyWidget is of type T.)

Thanks to the possibility of operator overloading, neither of these constraints need be satisfied. Yes, T must support a size member function, though it's worth mentioning that the function might be inherited from a base class. But this member function need not return an integral type. It need not even return a numeric type. For that matter, it need not even return a type for which operator> is defined! All it needs to do is return an object of some type X such that there is an operator> that can be called with an object of type X and an int (because 10 is of type int). The operator> need not take a parameter of type X, because it could take a parameter of type Y, and that would be okay as long as there were an implicit conversion from objects of type X to objects of type Y!

Similarly, there is no requirement that T support operator!=, because it would be just as acceptable for operator!= to take one object of type X and one object of type Y. As long as T can be converted to X and someNastyWidget's type can be converted to Y, the call to operator!= would be valid.

(As an aside, this analysis doesn't take into account the possibility that operator&& could be overloaded, thus changing the meaning of the above expression from a conjunction to something potentially quite different.)

5、The implicit interfaces imposed on a template's parameters are just as real as the explicit interfaces imposed on a class's objects, and both are checked during compilation.

6、Things to Remember

  • Both classes and templates support interfaces and polymorphism.

  • For classes, interfaces are explicit and centered on function signatures. Polymorphism occurs at runtime through virtual functions.

  • For template parameters, interfaces are implicit and based on valid expressions. Polymorphism occurs during compilation through template instantiation and function overloading resolution.

<Item 42> Understand the two meanings of typename

7、typename是比较晚才进入C++标准的,之前都是使用class关键字。下面代码并没有任何差别。When declaring a template type parameter, class and typename mean exactly the same thing. Some programmers prefer class all the time, because it's easier to type. Others (including me) prefer typename, because it suggests that the parameter need not be a class type. A few developers employ typename when any type is allowed and reserve class for when only user-defined types are acceptable. But from C++'s point of view, class and typename mean exactly the same thing when declaring a template parameter.

template<class T> class Widget;                 // uses "class"
template<typename T> class Widget; // uses "typename"

8、Names in a template that are dependent on a template parameter are called dependent names. When a dependent name is nested inside a class, I call it a nested dependent name. C::const_iterator is  a nested dependent type name;The other local variable in print2nd, value, has type int. int is a name that does not depend on any template parameter. Such names are known as non-dependent names。下面代码不会边缘成功

template<typename C>                            // print 2nd element in
void print2nd(const C& container) // container;
{ // this is not valid C++!
if (container.size() >= ) {
C::const_iterator iter(container.begin()); // get iterator to 1st element
++iter; // move iter to 2nd element
int value = *iter; // copy that element to an int
std::cout << value; // print the int
}
}

9、对于下面的代码可能会有歧义。This looks like we're declaring x as a local variable that's a pointer to a C::const_iterator. But it looks that way only because we "know" that C::const_iterator is a type. But what if C::const_iterator weren't a type? What if C had a static data member that happened to be named const_iterator, and what if x happened to be the name of a global variable? In that case, the code above wouldn't declare a local variable, it would be a multiplication of C::const_iterator by x!

template<typename C>
void print2nd(const C& container)
{
C::const_iterator * x;
...
}

10、C++ has a rule to resolve this ambiguity: if the parser encounters a nested dependent name in a template, it assumes that the name is not a type unless you tell it otherwise. By default, nested dependent names are not types. (There is an exception to this rule that I'll get to in a moment.)

template<typename C>                           // this is valid C++
void print2nd(const C& container)
{
if (container.size() >= ) {
typename C::const_iterator iter(container.begin());
...
}
}

The general rule is simple: anytime you refer to a nested dependent type name in a template, you must immediately precede it by the word typename. (Again, I'll describe an exception shortly.)

11、typename should be used to identify only nested dependent type names; other names shouldn't have it. For example, here's a function template that takes both a container and an iterator into that container:

template<typename C>                   // typename allowed (as is "class")
void f(const C& container, // typename not allowed
typename C::iterator iter); // typename required

12、The exception to the "typename must precede nested dependent type names" rule is that typename must not precede nested dependent type names in a list of base classes or as a base class identifier in a member initialization list. For example:

template<typename T>
class Derived: public Base<T>::Nested { // base class list: typename not
public: // allowed
explicit Derived(int x)
: Base<T>::Nested(x) // base class identifier in mem
{ // init. list: typename not allowed
typename Base<T>::Nested temp; // use of nested dependent type
... // name not in a base class list or
} // as a base class identifier in a
... // mem. init. list: typename required
};

13、a common convention is for the typedef name to be the same as the traits(see Item 47) member name

template<typename IterT>
void workWithIterator(IterT iter)
{
typename std::iterator_traits<IterT>::value_type temp(*iter);
...
}
template<typename IterT>
void workWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type;
value_type temp(*iter);
...
}

14、As a closing note, I should mention that enforcement of the rules surrounding typename vary from compiler to compiler. Some compilers accept code where typename is required but missing; some accept code where typename is present but not allowed; and a few (usually older ones) reject typename where it's present and required. This means that the interaction of typename and nested dependent type names can lead to some mild portability headaches.

15、Things to Remember

  • When declaring template parameters, class and typename are interchangeable.

  • Use typename to identify nested dependent type names, except in base class lists or as a base class identifier in a member initialization list.

<Item 43> Know how to access names in templatized base classes

16、如下代码 Note how the message-sending function in the derived class has a different name (sendClearMsg) from the one in its base class (there, it's called sendClear). That's good design, because it side-steps the issue of hiding inherited names (see Item 33) as well as the problems inherent in redefining an inherited non-virtual function (see Item 36). The problem is that when compilers encounter the definition for the class template LoggingMsgSender, they don't know what class it inherits from. Sure, it's MsgSender<Company>, but Company is a template parameter, one that won't be known until later (when LoggingMsgSender is instantiated). Without knowing what Company is, there's no way to know what the class MsgSender<Company> looks like. In particular, there's no way to know if it has a sendClear function.二段式名字查找(dreaded two phase name lookup 可以参考http://www.codeproject.com/Articles/8482/Standard-Features-Missing-From-VC-Part-III-Two)的结果。Note the "template <>" syntax at the beginning of this class definition. It signifies that this is neither a template nor a standalone class. Rather, it's a specialized version of the MsgSender template to be used when the template argument is CompanyZ. This is known as a total template specialization: the template MsgSender is specialized for the type CompanyZ, and the specialization is total — once the type parameter has been defined to be CompanyZ, no other aspect of the template's parameters can vary.(模板的全特化和偏特化,此处是全特化)

class CompanyA {
public:
...
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
...
}; class CompanyB {
public:
...
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
...
};
class CompanyZ { // this class offers no
public: // sendCleartext function
...
void sendEncrypted(const std::string& msg);
...
};
... // classes for other companies class MsgInfo { ... }; // class for holding information
// used to create a message
template<typename Company>
class MsgSender {
public:
... // ctors, dtor, etc.
void sendClear(const MsgInfo& info)
{
std::string msg;
create msg from info;
Company c;
c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& info) // similar to sendClear, except
{ ... } // calls c.sendEncrypted
};
template<> // a total specialization of
class MsgSender<CompanyZ> { // MsgSender; the same as the
public: // general template, except
... // sendCleartext is omitted
void sendSecret(const MsgInfo& info)
{ ... }
};
template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:
...
void sendClearMsg(const MsgInfo& info)
{
write "before sending" info to the log;
sendClear(info); // if Company == CompanyZ,
// this function doesn't exist!
write "after sending" info to the log;
}
...
};

17、有三种方法解决上述问题

  • First, you can preface calls to base class functions with "this->":
template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:
...
void sendClearMsg(const MsgInfo& info)
{
write "before sending" info to the log;
this->sendClear(info); // okay, assumes that
// sendClear will be inherited
write "after sending" info to the log;
}
...
};
  • Second, you can employ a using declaration, a solution that should strike you as familiar if you've read Item 33. That Item explains how using declarations bring hidden base class names into a derived class's scope. We can therefore write sendClearMsg like this:(Although a using declaration will work both here and in Item 33, the problems being solved are different. Here, the situation isn't that base class names are hidden by derived class names, it's that compilers don't search base class scopes unless we tell them to.)
template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:
using MsgSender<Company>::sendClear; // tell compilers to assume
... // that sendClear is in the
// base class
void sendClearMsg(const MsgInfo& info)
{
...
sendClear(info); // okay, assumes that
... // sendClear will be inherited
}
...
};
  • A final way to get your code to compile is to explicitly specify that the function being called is in the base class:This is generally the least desirable way to solve the problem, because if the function being called is virtual, explicit qualification turns off the virtual binding behavior.
template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:
...
void sendClearMsg(const MsgInfo& info)
{
...
MsgSender<Company>::sendClear(info); // okay, assumes that
... // sendClear will be
} //inherited
...
};

18、From a name visibility point of view, each of these approaches does the same thing: it promises compilers that any subsequent specializations of the base class template will support the interface offered by the general template. Such a promise is all compilers need when they parse a derived class template like LoggingMsgSender, but if the promise turns out to be unfounded, the truth will emerge during subsequent compilation. Fundamentally, the issue is whether compilers will diagnose invalid references to base class members sooner (when derived class template definitions are parsed) or later (when those templates are instantiated with specific template arguments). C++'s policy is to prefer early diagnoses, and that's why it assumes it knows nothing about the contents of base classes when those classes are instantiated from templates.

19、Things to Remember

  • In derived class templates, refer to names in base class templates via a "this->" prefix, via using declarations, or via an explicit base class qualification.

<Item 44> Factor parameter-independent code out of templates

20、commonality and variability analysis共性和变性分析是消除重复的重要方法,但是在泛型编程中,重复代码相对难以识别

21、This template takes a type parameter, T, but it also takes a parameter of type size_t — a non-type parameter. Non-type parameters are less common than type parameters, but they're completely legal, and, as in this example, they can be quite natural.Two copies of invert will be instantiated here. The functions won't be identical, because one will work on 5x5 matrices and one will work on 10 x 10 matrices, but other than the constants 5 and 10, the two functions will be the same. This is a classic way for template-induced code bloat to arise.

template<typename T,           // template for n x n matrices of
std::size_t n> // objects of type T; see below for info
class SquareMatrix { // on the size_t parameter
public:
...
void invert(); // invert the matrix in place
};
SquareMatrix<double, > sm1;
...
sm1.invert(); // call SquareMatrix<double, 5>::invert
SquareMatrix<double, > sm2;
...
sm2.invert(); // call SquareMatrix<double, 10>::invert

2、

<Effective C++>读书摘要--Templates and Generic Programming<一>的更多相关文章

  1. <Effective C++>读书摘要--Implementations<二>

    <Item29> Strive for exception-safe code. 1.如下面的代码 class PrettyMenu { public: ... void changeBa ...

  2. <Effective C++>读书摘要--Introduction

    Introduction 1.Learning the fundamentals of a programming language is one thing; learning how to des ...

  3. <Effective C++>读书摘要--Implementations<一>

    1.For the most part, coming up with appropriate definitions for your classes (and class templates) a ...

  4. <Effective C++>读书摘要--Designs and Declarations<三>

    <Item 22> Declare data members private 1.使数据成员private,保持了语法的一致性,client不会为访问一个数据成员是否需要使用括号进行函数调 ...

  5. <Effective C++>读书摘要--Designs and Declarations<一>

    <Item 18> Make interfaces easy to use correctly and hard to use incorrectly 1.That being the c ...

  6. <Effective C++>读书摘要--Accustoming Youself to C++

    <Item 1>View C++ as a federation of languages. 1.把C++看成4种子语言组成,即C.Object-Oriented C++.Template ...

  7. <Effective C++>读书摘要--Inheritance and Object-Oriented Design<二>

    <Item 36> Never redefine an inherited non-virtual function 1.如下代码通过不同指针调用同一个对象的同一个函数会产生不同的行为Th ...

  8. <Effective C++>读书摘要--Inheritance and Object-Oriented Design<一>

    1.Furthermore, I explain what the different features in C++ really mean — what you are really expres ...

  9. <Effective C++>读书摘要--Designs and Declarations<二>

    <Item 20> Prefer pass-by-reference-to-const to pass-by-value 1.By default, C++ passes objects ...

随机推荐

  1. 【tp5.1】通过PHPExcel实现导入excel表格

    1.上github下载PHPExcel,链接:https://github.com/PHPOffice/PHPExcel 2.下载解压后,将Classes改名为PHPExcel如图 3.将文件夹复制到 ...

  2. 在我的职业生涯中,没有一种技能比 SQL 更有用!

    作者 | Craig Kerstiens 译者 | 阿拉丁 创业公司 CitusData(CitusData 是一家将 PostgreSQL 商业化的初创企业,也是 PostgreSQL 社区领导者, ...

  3. ssh_key登录服务器,免密码登录

    最近使用ssh远程登录服务器每次都要输入密码实在是太麻烦了,我这个懒人就想到了查了一下怎么用ssh-key来实现我的梦想. 使用密钥来登录 原理如下: 客户端向服务器发出请求.服务器收到请求之后,先在 ...

  4. BIOS简单讲解

    学习链接: http://www.xuetangx.com/courses/course-v1:TsinghuaX+30240243X+sp/courseware/1d95cdf6f0e9434488 ...

  5. 慎用静态类static class

    偶然翻到一篇有趣的帖子: class - When to Use Static Classes in C# - Stack Overflowhttp://stackoverflow.com/quest ...

  6. 数据结构之栈和队列及其Java实现

    栈和队列是数据结构中非常常见和基础的线性表,在某些场合栈和队列使用很多,因此本篇主要介绍栈和队列,并用Java实现基本的栈和队列,同时用栈和队列相互实现. 栈:栈是一种基于“后进先出”策略的线性表.在 ...

  7. 20145202mc《计算机病毒》实践3

    网站检测 http://www.virscan.org/ lab01-02.exe lab01-03.exe 分析这两个文件是否加壳了: Lab01-02.exe lab01-03.exe 查看两个样 ...

  8. agc 027 B - Garbage Collector

    B - Garbage Collector https://agc027.contest.atcoder.jp/tasks/agc027_b 题意: x坐标轴上n个垃圾,有一个机器人在从原点,要清扫垃 ...

  9. Ruby数据类型

    数字类型 书写整数时,可以根据需要在整数之间任意加入下划线而不会影响数字的值 a=123_45_78 puts a # => 12345678 to_i 截掉小数点之后的数字取整 内置Math模 ...

  10. DSP5509的GPIO学习-第5篇

    1. 使用CCS V6.1版本,目前已经不局限于仅仅把实验搞清楚了,要深入去探究内部的原理,本章看下GPIO实验 2. 在CCS启动的时候,提示,这个问题是什么,XDAIS是什么?XDAIS (eXp ...