C++ Primer 4 第十六章 模板和泛型编程

C++Primer第五版 习题答案 目录 C++Primer第五版 习题答案 本文当作我学习C++的一个记录,这里的习题答案并不是标准答案,仅仅是我的理解,如有错误,请大家指出,谢谢! 我参考的文章会在文末列出,谢谢大家。 第一章 ... 阅读详情
 

第十六章 模板和泛型编程

1. 模板定义

定义函数模板:函数模板是一个独立于类型的函数,可作为一种方式,产生函数的特定类型版本。模板定义以关键字 template 开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,形参之间以逗号分隔。模板形参表不能为空。

          // implement strcmp-like generic compare function

     // returns 0 if the values are equal, 1 if v1 is larger, -1 if v1 is smaller

     template <typename T>

     int compare(const T &v1, const T &v2)

     {

         if (v1 < v2) return -1;

         if (v2 < v1) return 1;

         return 0;

     }

模板形参表:模板形参表示可以在类或函数的定义中使用的类型或值。表示哪个实际类型由编译器根据所用的函数而确定。模板形参可以是表示类型的类型形参,也可以是表示常量表达式的非类型形参。非类型形参跟在类型说明符之后声明,类型形参跟在关键字 class 或 typename 之后定义,例如,class T 是名为 T 的类型形参,在这里 class 和 typename 没有区别。

         编译器将确定用什么类型代替每个类型形参,以及用什么值代替每个非类型形参。推导出实际模板实参后,编译器使用实参代替相应的模板形参产生编译该版本的函数:

         cout << compare(1, 0) << endl;      // T is int;

         string s1 = "hi", s2 = "world";

         cout << compare(s1, s2) << endl;  // T is string;

inline 函数模板:函数模板可以用与非模板函数一样的方式声明为 inline。说明符放在模板形参表之后、返回类型之前,不能放在关键字 template 之前。

     // ok: inline specifier follows template parameter list

     template <typename T> inline T min(const T&, const T&);

     // error: incorrect placement of inline specifier

     inline template <typename T> T min(const T&, const T&);

定义类模板:

         template <class Type> class Queue {

     public:

         Queue ();                // default constructor

         Type &front ();          // return element from head of Queue

         const Type &front () const;

         void push (const Type &); // add element to back of Queue

         void pop();              // remove element from head of Queue

         bool empty() const;      // true if no elements in the Queue

     private:

         // ...

     };

使用类模板时,必须为模板形参显式指定实参:

     Queue<int> qi;                 // Queue that holds ints

     Queue< vector<double> > qc;    // Queue that holds vectors of doubles

     Queue<string> qs;              // Queue that holds strings

模板形参作用域:模板形参的名字可以在声明为模板形参之后直到模板声明或定义的末尾处使用。模板形参遵循常规名字屏蔽规则。与全局作用域中声明的对象、函数或类型同名的模板形参会屏蔽全局名字:

     typedef double T;

     template <class T> T calc(const T &a, const T &b)

     {

          // tmp has the type of the template parameter T

          // not that of the global typedef

          T tmp = a;

          // ...

          return tmp;

     }

使用模板形参名字的限制:用作模板形参的名字不能在模板内部重用。

     template <class T> T calc(const T &a, const T &b)

     {

         typedef double T; // error: redeclares template parameter T

         T tmp = a;

         // ...

         return tmp;

     }

这一限制还意味着模板形参的名字只能在同一模板形参表中使用一次:

     // error: illegal reuse of template parameter name V

     template <class V, class V> V calc(const V&, const V&) ;

模板声明:像其他任意函数或类一样,对于模板可以只声明而不定义。声明必须指出函数或类是一个模板:

         // declares compare but does not define it

     template <class T> int compare(const T&, const T&) ;

同一模板的声明和定义中,模板形参的名字不必相同。

     // all three uses of calc refer to the same function template

     // forward declarations of the template

     template <class T> T calc(const T&, const T&) ;

     template <class U> U calc(const U&, const U&) ;

     // actual definition of the template

     template <class Type>

     Type calc(const Type& a, const Type& b) { /* ... */ }

每个模板类型形参前面必须带上关键字 class 或 typename,每个非类型形参前面必须带上类型名字,省略关键字或类型说明符是错误的:

     // error: must precede U by either typename or class

     template <typename T, U> T calc (const T&, const U&) ;

typename 与 class 的区别:在函数模板形参表中,关键字 typename 和 class 具有相同含义,可以互换使用,两个关键字都可以在同一模板形参表中使用:

     // ok: no distinction between typename and class in template parameter list

     template <typename T, class U> calc (const T&, const U&);

         关键字 typename 是作为标准 C++ 的组成部分加入到 C++ 中的,因此旧的程序更有可能只用关键字 class。

通过在成员名前加上关键字 typename 作为前缀,可以告诉编译器将成员当作类型。默认情况下,编译器假定这样的名字指定数据成员,而不是类型。

         template <class Parm, class U>

     Parm fcn(Parm* array, U value)

     {

         typename Parm::size_type * p; // ok: declares p to be a pointer

     }

这一声明给用实例化 fcn 的类型增加了一个职责:那些类型必须具有名为 size_type 的成员,而且该成员是一个类型。

非类型模板形参:

         在调用函数时非类型形参将用值代替,值的类型在模板形参表中指定。下面的函数模板声明了 array_init 是一个含有一个类型模板形参和一个非类型模板形参的函数模板。函数本身接受一个形参,该形参是数组的引用

     // initialize elements of an array to zero

     template <class T, size_t N> void array_init(T (&parm)[N])

     {

         for (size_t i = 0; i != N; ++i) {

             parm[i] = 0;

         }

     }

         int x[42];

     double y[10];

     array_init(x);  // instantiates array_init(int(&)[42]

     array_init(y);  // instantiates array_init(double(&)[10]

在函数模板内部完成的操作限制了可用于实例化该函数的类型。程序员的责任是,保证用作函数实参的类型实际上支持所用的任意操作,以及保证在模板使用哪些操作的环境中那些操作运行正常。

编写模板代码时,对实参类型的要求尽可能少是很有益的。编写泛型代码的两个重要原则:

         1)模板的形参是 const 引用。

         2)函数体中的测试只用 < 比较。

链接时的编译时错误:编译模板时,编译器可能会在三个阶段中标识错误:第一阶段是编译模板定义本身时。在这个阶段中编译器一般不能发现许多错误,可以检测到诸如漏掉分号或变量名拼写错误一类的语法错误。第二个错误检测时间是在编译器见到模板的使用时。在这个阶段,编译器仍没有很多检查可做。对于函数模板的调用,许多编译器只检查实参的数目和类型是否恰当,编译器可以检测到实参太多或太少,也可以检测到假定类型相同的两个实参是否真地类型相同。对于类模板,编译器可以检测提供的模板实参的正确数目。产生错误的第三个时间是在实例化的时候,只有在这个时候可以发现类型相关的错误。根据编译器管理实例化的方式,有可能在链接时报告这些错误。要认识到编译模板定义的时候,对程序是否有效所知不多。类似地,甚至可能会在已经成功编译了使用模板的每个文件之后出现编译错误。只在实例化期间检测错误的情况很少,错误检测可能发生在链接时。

2. 实例化

类模板不定义类型,只有特定的实例才定义了类型。例如,Queue 不是类型,而 Queue<int> 或 Queue<string> 是类型。

模板类型形参可以用作一个以上函数形参的类型。在这种情况下,模板类型推断必须为每个对应的函数实参产生相同的模板实参类型。如果推断的类型不匹配,则调用将会出错:

     template <typename T>

     int compare(const T& v1, const T& v2)

     {

         if (v1 < v2) return -1;

         if (v2 < v1) return 1;

         return 0;

     }

     int main()

     {

         short si;

         // error: cannot instantiate compare(short, int)

         // must be: compare(short, short) or

         // compare(int, int)

         compare(si, 1024);

         return 0;

     }

如果 compare 的设计者想要允许实参的常规转换,则函数必须用两个类型形参来定义,但是,比较那些类型的值的 < 操作符必须存在:

     // argument types can differ, but must be compatible

     template <typename A, typename B>

     int compare(const A& v1, const B& v2)

     {

         if (v1 < v2) return -1;

         if (v2 < v1) return 1;

         return 0;

     }

类型形参的实参的受限转换:一般而论,不会转换实参以匹配已有的实例化,相反,会产生新的实例。除了产生新的实例化之外,编译器只会执行两种转换:

         1)const 转换:接受 const 引用或 const 指针的函数可以分别用非 const 对象的引用或指针来调用,无须产生新的实例化。如果函数接受非引用类型,形参类型实参都忽略 const,即,无论传递 const 或非 const 对象给接受非引用类型的函数,都使用相同的实例化。

         2)数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。

例如,考虑对函数 fobj 和 fref 的调用。fobj 函数复制它的形参,而 fref 的形参是引用:

     template <typename T> T fobj(T, T); // arguments are copied

     template <typename T>

     T fref(const T&, const T&);       // reference arguments

     string s1("a value");

     const string s2("another value");

     fobj(s1, s2);     // ok: calls f(string, string), const is ignored

     fref(s1, s2);     // ok: non const object s1 converted to const reference

     int a[10], b[42];

     fobj(a, b); // ok: calls f(int*, int*)

     fref(a, b); // error: array types don't match; arguments aren't converted to pointers

在第二种情况中,将传递不同长度的数组实参。fref 的调用是非法的,当形参为引用时,数组不能转换为指针,a 和 b 的类型不匹配,所以调用将出错。

类型转换的限制只适用于类型为模板形参的那些实参。用普通类型定义的形参可以使用常规转换。

模板实参推断与函数指针:可以使用函数模板对函数指针进行初始化或赋值,这样做的时候,编译器使用指针的类型实例化具有适当模板实参的模板版本。

          template <typename T> int compare(const T&, const T&);

     int (*pf1) (const int&, const int&) = compare;

pf1 的类型是一个指针,指向“接受两个 const int& 类型形参并返回 int 值的函数”,形参的类型决定了 T 的模板实参的类型,T 的模板实参为 int 型,指针 pf1 引用的是将 T 绑定到 int 的实例化。

获取函数模板实例化的地址的时候,上下文必须是这样的:它允许为每个模板形参确定唯一的类型或值:

          void func(int(*) (const string&, const string&));

     void func(int(*) (const int&, const int&));

     func(compare); // error: which instantiation of compare?

函数模板的显式实参:在某些情况下,不可能推断模板实参的类型。当函数的返回类型必须与形参表中所用的所有类型都不同时,最常出现这一问题。在这种情况下,有必要覆盖模板实参推断机制,并显式指定为模板形参所用的类型或值。

         template <class T, class U> ??? sum(T, U);

解决这一问题的一个办法,可能是强制 sum 的调用者将较小的类型强制转换为希望作为结果使用的类型:

     // ok: now either T or U works as return type

     int i; short s;

     sum(static_cast<int>(s), i); // ok: instantiates int sum(int, int)

在返回类型中使用类型形参:指定返回类型的一种方式是引入第三个模板形参,它必须由调用者显式指定:

     template <class T1, class T2, class T3>

     T1 sum(T2, T3);

没有实参的类型可用于推断 T1 的类型,相反,调用者必须在每次调用 sum 时为该形参显式提供实参。在以逗号分隔、用尖括号括住的列表中指定显式模板实参。显式模板类型的列表出现在函数名之后、实参表之前:

     long val3 = sum<long>(i, lng); // ok: calls long sum(int, long)

这一调用显式指定 T1 的类型,编译器从调用中传递的实参推断 T2 和 T3 的类型。

显式模板实参从左至右对应模板形参相匹配,假如可以从函数形参推断,则结尾(最右边)形参的显式模板实参可以省略。如果这样编写 sum 函数:

     template <class T1, class T2, class T3>

     T3 alternative_sum(T2, T1);

则总是必须为所有三个形参指定实参:

     // error: can't infer initial template parameters

     long val3 = alternative_sum<long>(i, lng);

     // ok: All three parameters explicitly specified

     long val2 = alternative_sum<long, int, long>(i, lng);

显式实参与函数模板的指针:通过使用显式模板实参能够消除二义性:

     template <typename T> int compare(const T&, const T&);

     // overloaded versions of func; each take a different function pointer type

     void func(int(*) (const string&, const string&));

     void func(int(*) (const int&, const int&));

     func(compare<int>); // ok: explicitly specify which version of compare

3. 模板编译模型

当编译器看到模板定义的时候,它不立即产生代码。只有在看到用到模板时,如调用了函数模板或调用了类模板的对象的时候,编译器才产生特定类型的模板实例。

一般而言,当调用函数的时候,编译器只需要看到函数的声明。类似地,定义类类型的对象时,类定义必须可用,但成员函数的定义不是必须存在的。因此,应该将类定义和函数声明放在头文件中,而普通函数和类成员函数的定义放在源文件中。模板则不同:要进行实例化,编译器必须能够访问定义模板的源代码。当调用函数模板或类模板的成员函数的时候,编译器需要函数定义,需要那些通常放在源文件中的代码。

标准 C++ 为编译模板代码定义了两种模型。在两种模型中,构造程序的方式很大程度上是相同的:类定义和函数声明放在头文件中,而函数定义和成员定义放在源文件中。两种模型的不同在于,编译器怎样使用来自源文件的定义。

包含编译模型:编译器必须看到用到的所有模板的定义。一般而言,可以通过在声明函数模板或类模板的头文件中添加一条 #include 指示使定义可用,该 #include 引入了包含相关定义的源文件。

分别编译模型:编译器会为我们跟踪相关的模板定义。但是,我们必须让编译器知道要记住给定的模板定义,可以使用 export 关键字来做这件事。export 关键字能够指明给定的定义可能会需要在其他文件中产生实例化。在一个程序中,一个模板只能定义为导出一次。编译器在需要产生这些实例化时计算出怎样定位模板定义。export 关键字不必在模板声明中出现。

         export template <typename Type>

     Type sum(Type t1, Type t2) /* ...*/

这个函数模板的声明像通常一样应放在头文件中,声明不必指定 export。相反,应该在类的实现文件中使用 export:

     // class template header goes in shared header file

     template <class Type> class Queue { ... };

     // Queue.ccimplementation file declares Queue as exported

     export template <class Type> class Queue;

     #include "Queue.h"

     // Queue member definitions

导出类的成员将自动声明为导出的。也可以将类模板的个别成员声明为导出的,在这种情况下,关键字 export 不在类模板本身指定,而是只在被导出的特定成员定义上指定。导出成员函数的定义不必在使用成员时可见。任意非导出成员的定义必须像在包含模型中一样对待:定义应放在定义类模板的头文件中。

4. 类模板成员(Queue 类模板的具体实现)【Coding】

5. 一个泛型句柄类

Handle 类行为类似于指针:复制 Handle 对象将不会复制基础对象,复制之后,两个 Handle 对象将引用同一基础对象。要创建 Handle 对象,用户需要传递属于由 Handle 管理的类型(或从该类型派生的类型)的动态分配对象的地址,从此刻起,Handle 将“拥有”这个对象。而且,一旦不再有任意 Handle 对象与该对象关联,Handle 类将负责删除该对象。

         template <class T> class Handle {

     public:

         // unbound handle

         Handle(T *p = 0): ptr(p), use(new size_t(1)) { }

         // overloaded operators to support pointer behavior

         T& operator*();

         T* operator->();

         const T& operator*() const;

         const T* operator->() const;

         // copy control: normal pointer behavior, but last Handle deletes the object

         Handle(const Handle& h): ptr(h.ptr), use(h.use)

                                             { ++*use; }

         Handle& operator=(const Handle&);

         ~Handle() { rem_ref(); }

     private:

         T* ptr;          // shared object

         size_t *use;     // count of how many Handle spointto *ptr

         void rem_ref()

             { if (--*use == 0) { delete ptr; delete use; } }

     };

 

         template <class T>

     inline Handle<T>& Handle<T>::operator=(const Handle &rhs)

     {

         ++*rhs.use;      // protect against self-assignment

         rem_ref();       // decrement use count and delete pointers if needed

         ptr = rhs.ptr;

         use = rhs.use;

         return *this;

     }

         template <class T> inline T& Handle<T>::operator*()

     {

         if (ptr) return *ptr;

         throw std::runtime_error

                        ("dereference of unbound Handle");

     }

     template <class T> inline T* Handle<T>::operator->()

     {

         if (ptr) return ptr;

         throw std::runtime_error

                        ("access through unbound Handle");

     }

分配一个 int 对象,并将一个 Handle 对象绑定到新分配的 int 对象来说明 Handle 的行为:

     { // new scope

       // user allocates but must not delete the object to which the Handle is attached

       Handle<int> hp(new int(42));

       { // new scope

           Handle<int> hp2 = hp; // copies pointer; use count incremented

           cout << *hp << " " << *hp2 << endl; // prints 42 42

           *hp2 = 10;           // changes value of shared underlying int

       }   // hp2 goes out of scope; use count is decremented

       cout << *hp << endl; // prints 10

     } // hp goes out of scope; its destructor deletes the int

即使是 Handle 的用户分配了 int 对象,Handle 析构函数也将删除它。在外层代码块末尾最后一个 Handle 对象超出作用域时,删除该 int 对象。为了访问基础对象,应用了 Handle 的 * 操作符,该操作符返回对基础 int 对象的引用。

使用 Handle 对象对指针进行使用计数:

         class Sales_item {

     public:

         // default constructor: unbound handle

         Sales_item(): h() { }

         // copy item and attach handle to the copy

         Sales_item(const Item_base &item): h(item.clone()) { }

         // no copy control members: synthesized versions work

         // member access operators: forward their work to the Handle class

         const Item_base& operator*() const { return *h; }

         const Item_base* operator->() const

                                { return h.operator->(); }

     private:

         Handle<Item_base> h; // use-counted handle

     };

基于 Handle 的 Sales_item 版本有一个数据成员,该数据成员是关联传给构造函数的 Item_base 对象的副本上的 Handle 对象。因为 Sales_item 的这个版本没有指针成员,所以不需要复制控制成员,Sales_item 的这个版本可以安全地使用合成的复制控制成员。管理使用计数和相关 Item_base 对象的工作在 Handle 内部完成。

double Basket::total() const

     {

         double sum = 0.0; // holds the running total

         /* find each set of items with the same isbn and calculate

          * the net price for that quantity of items

          * iter refers to first copy of each book in the set

          * upper_boundrefers to next element with a different isbn

          */

         for (const_iter iter = items.begin();

                         iter != items.end();

                         iter = items.upper_bound(*iter))

         {

             // we know there's at least one element with this key in the Basket

             // virtual call to net_priceapplies appropriate discounts, if any

             sum += (*iter)->net_price(items.count(*iter));

         }

         return sum;

     }

6. 模板特化(高级主题)

7. 重载与函数模板

函数模板可以重载:可以定义有相同名字但形参数目或类型不同的多个函数模板,也可以定义与函数模板有相同名字的普通非模板函数。

如果重载函数中既有普通函数又有函数模板,确定函数调用的步骤如下:

         1)为这个函数名建立候选函数集合,包括:

                   a. 与被调用函数名字相同的任意普通函数.

                   b. 任意函数模板实例化,在其中,模板实参推断发现了与调用中所用函数实参相匹配的模板实参.

         2) 确定哪些普通函数是可行的(如果有可行函数的话)。候选集合中的每个模板实例都是可行的,因为模板实参推断保证函数可以被调用.

         3) 如果需要转换来进行调用,根据转换的种类排列可靠函数,记住,调用模板函数实例所允许的转换是有限的.

                   a. 如果只有一个函数可选,就调用这个函数.

                   b. 如果调用有二义性,从可行函数集合中去掉所有函数模板实例.

         4) 重新排列去掉函数模板实例的可行函数.

                   a. 如果只有一个函数可选,就调用这个函数.

                   b. 否则,调用有二义性.

// compares two objects

     template <typename T> int compare(const T&, const T&);

     // compares elements in two sequences

     template <class U, class V> int compare(U, U, V);

     // plain functions to handle C-style character strings

     int compare(const char*, const char*);

可以在不同类型上调用这些函数:

// calls compare(const T&, const T&) with T bound to int

     compare(1, 0);

     // calls compare(U, U, V), with U and V bound to vector<int>::iterator

     vector<int> ivec1(10), ivec2(20);

     compare(ivec1.begin(), ivec1.end(), ivec2.begin());

     int ia1[] = {0,1,2,3,4,5,6,7,8,9};

     // calls compare(U, U, V) with U bound to int*

     // and V bound to vector<int>::iterator

     compare(ia1, ia1 + 10, ivec1.begin());

     // calls the ordinary function taking const char* parameters

     const char const_arr1[] = "world", const_arr2[] = "hi";

     compare(const_arr1, const_arr2);

     // calls the ordinary function taking const char* parameters

     char ch_arr1[] = "world", ch_arr2[] = "hi";

     compare(ch_arr1, ch_arr2);

转换与重载的函数模板:

         char *p1 = ch_arr1, *p2 = ch_arr2;

     compare(p1, p2);

在这个例子中,将 char* 绑定到 T 的函数模板与该调用完全匹配。普通版本仍然需要从 char* 到 const char* 的转换,所以优先选择函数模板.

当匹配同样好时,非模板版本优先.

定义函数模板特化几乎总是比使用非模板版本更好.

 

typedef用法详解  也许新手用这个关键字不多,但它却是一个很有用的关键字,可以使代码模块化程度更好(即与其它代码的关联较少),在C++中还是实现Traits技术的基础,也是模板编程的基本语法之一。若说变量定义是为变量命名,而typedef(或称为类定义)就是为类命名。既然都是命名,那就会有很多类似的地方。而变量定义我想大家都会使用,因此类定义也必然会使用。类定义的语法可以归结为一句话:只要在变量定义前面加上 阅读详情

相关推荐

typedef的一些使用总结

目录 第一种用法:给变量定义别名 第二种用法:typedef原变量类别名 [例1]typedef double MyDouble; // 声明一个类别名 [例2]typedef double* PMyDouble;// 声明一个类别名 [例3] typedef int (Func)(int, int); // 声明一个函数类 [例3] typedef int* Fu...

随意的风的专栏 2684

# 编写`Basket`类

编写Basket类 class Basket{ public: //Basket使用合成的默认构造函数拷贝控制成员 void add_item(const std::shared_ptr<Quote> &sale) {items.insert(sale);} //打印每本书的总价购物篮中所有书的总价 double total_receipt(std::ostream&) const

m0_68935893的博客 203

15.7 添加拷贝控制的Quote类

#pragma once #include <iostream> #include <string> class Quote { public: Quote() = default; Quote(const std::string &bookNo, double book_price); Quote(const Quote &); ...

小飞将的博客 270

C++ Primer 学习笔记_83_模板编程 --一个句柄类

模板编程 --一个句柄类 引言: 【小心地雷】 这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承模板。在熟悉了这些特性之后再研究这个例子也许会帮助。另一方面,这个例子还能很好地测试你对这些特性的理解程度。 前面示例的Sales_itemQuery两个类的使用计数的实现是相同的。这类问题非常适合于编程:可以定义类模板管理指针

figou22221的专栏 484

C++ Primer》学习笔记/习题答案 总目录

文章目录前言专栏C++学习笔记目录第一章 - 快速入门第二章 - 变量基本类第三章 - 标准库类第四章 - 数组指针第五章 - 表达式第六章 - 语句第七章 - 函数第八章 - 标准 IO 库第九章 - 顺序容器第十章 - 关联容器第十一章 - 算法第十二章 - 类第十三章 - 复制控制第十四章 - 重载操作符与转换第十五章 - 面向对象编程第十六章 - 模板编程第十七章 - 用...

种树最好的时间是10年前,其次是现在!!! 4万+

C++ Primer 总结索引 | 第十六章模板编程

定义模板模板实参的推断:类转换,std::move,转发;重载与模板;可变参数模板:包拓展,转发参数包;模板特例化

AsherGu的博客 1108

C++Primer第五版 习题答案 第十六章 模板编程(Templates and Generic Programming)

练习16.1 给出实例化的定义。 实例化:编译器处理过程,用实际的模板实参来生成模板的一个特殊实例,其中参数被替换为对应的实参。当函数模板被调用时,会自动根据传递给它的实参来实例化。而使用类模板时,则需要我们提供显式模板实参。 练习16.2 编写并测试你自己版本的 compare 函数。 #include <iostream> template<typename T&g...

shamozhizhoutx的博客 3596

C++Primer5th 第十六章 模板编程

第十六章 模板编程 16.1 定义模板 16.1.1 函数模板 实例化函数模板 模板参数 非类模板参数 inlineconstexpr的函数模板 编写类无关的代码 模板编译 模板大多数编译错误在实例化期间报告 16.1.2 类模板 定义类模板 实例化类模板模板的成员函数 类模板成员函数的实例化 类代码内简化模板类名的使用 类模板友元 一对一友好关系 通用特定的模板友好关系 模板别名 类模板的static成员 16.1.3 模板参数 模板参数与作用域 模板声明 .

梦三岁的博客 1165

C++Primer 第五版》——第十六章 模板编程

C++Primer 第五版》——第十六章 模板编程

KCDCY的博客 1406

C++ Primer(第五版)|练习题答案与解析(第十六章:模板编程)

C++ Primer(第五版)|练习题答案与解析(第十六章:模板编程) 本博客主要记录C++ Primer(第五版)中的练习题答案与解析。 参考:C++Primer C++ Primer 练习题16.1 给出实例化定义 P579,当调用一个函数模板时,编译器会使用实参的类来确定绑定到模版参数T上的类。之后编译器利用推断出的模版参数来实例化一个特定版本的函数,这个过程被称之为实例化。 ...

qq_24739717的博客 1557

C++ PRIMER 第十六章 模板编程

第四部分 面向对象编程编程   第十六章 模板编程   所谓编程是以独立于任何特定类的方式编写代码,使用程序时,我们需要提供具体程序实例所操作的类或值,模板编程的基础,使用模板时无须了解模板的定义,在编程中,我们所编写的类或函数能够多态地用于跨越编译时不相关的类,一个类或一个函数可以用来操纵多种类的对象   16.1 定义模板     16.1.1 定...

金色的省略号 318

c++ primer 第十六章模板编程

c++ primer 第十六章模板编程16.1 定义模板16.1.1 函数模板16.1.2 类模板16.1.3 模板参数16.1.4 成员模板16.1.5 控制实例化16.1.6 效率与灵活性16.2 模板实参推断16.2.1 类转换与模板参数16.2.2 函数模板显示实参16.2.3 尾置返回类与类转换16.2.4 函数指针与实参推断16.2.5 模板实参推断引用 16.1 定...

无所知的博客 679

C++Primer第十六章——模板编程

第十六章模板编程 面向对象编程(OOP)编程都能处理在编写程序时不知道类的情况,但不同的是OOP能处理类在程序运行前都未知的情况,而在编程中,在编译时就能获知类模板C++编程的基础 16.1 定义模板 1.函数模板 2.类模板 3.模板参数 4.成员模板 5.控制实例化 6.效率与灵活性 16.2 模板实参推断 16.3 重载与模板 16.4 可变参数模板 16.5 模板特例化 ...

whutmessi的博客 458

c++ primer读书笔记--第十六章 模板编程

c++ primer读书笔记–第十六章 模板编程件在编程中,我们所编写的类函数能够多态地用于跨越编译时不相关的类。一个类或一个函数可以用来操纵多种类的对象。标准库中的容器、迭代器算法是很好的编程的例子。16.1. 模板定义16.1.1. 定义函数模板template <typename T> int compare(const T &v1, const T &v2

梦想还是要有的 724

C++_Primer_学习笔记_第十六章模板编程

第十六章模板编程) 1).编程。例如,容器,迭代器,算法。提供一些信息进行实例化。可以在编译时就知道类模板编程的基础。 2).OOP,动态绑定,可以在运行时才知道类。 3).以上两者都可以处理编写程序时,不知道类的情况。 /1.定义模板 1).比较所有类的数据大小, 需要定义很多的重载函数,(我们所能想到的) 而且如果是用户自定义的类,这种策略就不可行。 2).比较函数,除了数据类,函数体,函数名,参数名,返回值类完全一样。 //1.函数模板 1).定义一个函数.

YRC的博客 577

C++ Primer —— 第十六章 模板编程笔记

文章目录第16章 模板编程定义模板函数模板模板模板参数成员模板控制实例化效率与灵活性模板实参推断类转换与模板参数函数模板显式实参尾置返回类与类转换函数指针实参推断模板实参推断引用理解std::move转发重载与模板可变参数模板编写可变参数函数模板包扩展转发参数包模板特例化术语表 第16章 模板编程 定义模板 函数模板 template<typename T> int compare(const T &v1, const T &v2) { if(

weixin_46698891的博客 624

C++ Primer 学习笔记 第十六章 模板编程

编程与OOP都能处理在编写程序时不知道类的情况,但编程在编译时就能火值类了。 模板编程的基础,它是创建一个类或函数的蓝图或公式。vector为类,find为函数。 当多个函数除了参数类外完全相同,可以用函数将多个函数简化为一个,以下为一个函数模板: template <typename T> int compare(const T &v1, c...

tus00000的博客 444

C++ Primer 第十六章 模板编程 重点解读

重点解读c++11模板编程特性,以及C++20concept特性

Dusong_的博客 1281
上一篇: C++ Primer 4 第十五章 面向对象编程
下一篇: C++ Primer 4 第十七章 用于大型程序的工具
Winston_wu
博客等级 码龄15年 72粉丝 258原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值