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. Python提高篇

    Python提高篇 1.模块 1)模块定义 模块就是py文件,可以是你自己写的,也可以是python当中自带的工具,当你在某个py文件下想要引用其他模块的功能,就需要你把你把该py文件导入. 2)导入 ...

  2. PHP中如何对二维数组按某个键值进行排序

    $arr=[     array(         'name'=>'张三',         'age'=>28     ),     array(         'name'=> ...

  3. Delphi7 GDI+学习

    Delphi7自带的绘图有锯齿,所以要学习GDI+ 主要是从这个网站学习 http://www.bianceng.com/Programming/Delphi/201212/34691.htm 相关控 ...

  4. python的基本知识

    1. python的简介    python的创始⼈人为吉多·范罗苏姆(Guido van Rossum).1989年年的圣诞节期间,吉多· 范罗苏姆为了了在阿姆斯特丹丹打发时间,决⼼心开发⼀个新的脚 ...

  5. python--模块之sys与python解释器交互模块

    作用:sys模块是与python解释器交互的一个接口.它提供了一系列有关python运行环境的变量和函数. 常用函数:import sys sys.argv #命令行参数list,第一个元素是程序本身 ...

  6. vimrc 配置

    " All system-wide defaults are set in $VIMRUNTIME/debian.vim and sourced by" the call to : ...

  7. 24-集成ASP.NETCore Identity采用EF

    1-增加IdentityServer4.AspNetIdentity nuget包 2- StartUp.cs启用增加相应的代码  .AddAspNetIdentity<ApplicationU ...

  8. Milking Order

    Milking Order 题意:给出m个描述状态,其中包含若干个边的关系,问最多能取x (x<=m)个状态,使得形成的图没有环.就是说取x个状态,用状态中的关系建边,其中不能有环. 题解:最大 ...

  9. 网络相关知识点:nginx相关概念

    今天我们来介绍一下反向代理以及负载均衡相关内容: 反向代理: 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求, 然后将请求转发给内部网络上的服务器,并将从 ...

  10. 苏州Uber优步司机奖励政策(4月2日~4月3日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...