C++ Primer 学习笔记_78_模板与泛型编程 -实例化[续]

模板与泛型编程

--实例化[续]

二、函数模板的显式实参

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

1、指定显式模板实参

如果函数形参类型不统一,该如何指定sum的返回类型?

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

此时使用任一形参调用都一定会在某些时候失败:

    sum(3, 4L); // 第二个类型较大,则应使用 U sum(T, U)
    sum(3L, 4); // 第一个类型较大,则应使用 T sum(T, U)

可以强制sum的调用者将较小的类型强制转换为希望作为结果使用的类型来解决此问题:

    int i;
    short s;
    sum(static_cast<int>(s), i);

2、在返回类型中使用类型形参

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

template <class T1,class T2,class T3>
T1 sum(T2,T3);

有一个问题:没有实参的类型可用于推断T1的类型,相反,调用者必须在每次调用sum时为该类型显式提供实参。

    int i;
    long lng;
    long val = sum<long>(i,lng);

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

【小结】

显式模板实参从左至右与对应模板形参相匹配,假如可以从函数形参推断,则只有结尾(最右边)形参的显式模板实参可以忽略。

如果这样编写sum函数:

template <class T1,class T2,class T3>
T3 alternative_sum(T2,T1);

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

    //没法识别模板实参的初始化
    long val = alternative_sum<long>(i,lng);    //Error
    long val2 = alternative_sum<long,int,long>(i,lng);  //OK

3、显式实参与函数模板指针

可以使用显式模板实参的另一个例子是第16.2.1节中有二义性程序,通过使用显式模板实参能够消除二义性:

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

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

func(compare<string>);

像前面一样,需要在调用中传递compare实例给名为func的重载函数。只查看不同版本func的形参表来选择传递compare的哪个实例是不可能的,两个不同的实例都可能满足该调用。显式模板形参需要指出应使用哪个compare实例以及调用哪个func函数。

//P542 习题16.23
    cout << max(3.14,5) << endl;	//Error
    cout << max<double>(5.56,4) << endl;	//OK
//习题16.24/25
template <typename T,typename U>
int compare(const T &val1,const U &val2)
{
    if (val1 < val2)
        return -1;
    if (val2 < val1)
        return 1;
    return 0;
}

int main()
{
    cout << compare(1,static_cast<int>(3.14)) << endl;
    cout << compare(3.14,static_cast<double>(3)) << endl;

    cout << compare<int>(1,3.14) << endl;
    cout << compare<double>(1,3.14) << endl;

    cout << compare<string,string>("Hello","word") << endl;
    cout << compare(string("hello"),string("Word")) << endl;
}
//习题16.26
template <typename T1,typename T2,typename T3>
T1 sum(T2,T3);

int main()
{
    double dobj1,dobj2;
    float fobj1,fobj2;
    char cobj1,cobj2;

    sum(dobj1,dobj2);   //Error
    sum<double,double,double>(fobj1,fobj2);	//OK
    sum<int,int>(cobj1,cobj2);	//OK
    sum<double,,double>(fobj1,fobj2);	//Error
}
时间: 2024-10-28 04:03:07

C++ Primer 学习笔记_78_模板与泛型编程 -实例化[续]的相关文章

C++ Primer 学习笔记_77_模板与泛型编程 --实例化

模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实际模板类型时实例化,函数模板在调用它或用它对函数指针进行初始化或赋值时实例化. 1.类的实例化 当编写Queue<int>qi时,编译器自己主动创建名为Queue<int>的类.实际上,编译器通过又一次编写Queue模板,用类型int取代模板形參的每次出现而创建Queue<int

C++ Primer 学习笔记_81_模板与泛型编程 --类模板成员[续1]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height

C++ Primer 学习笔记_82_模板与泛型编程 --类模板成员[续2]

模板与泛型编程 --类模板成员[续2] 六.完整的Queue类 Queue的完整定义: template <typename Type> class Queue; template <typename Type> ostream &operator<<(ostream &,const Queue<Type> &); template <typename Type> class QueueItem { friend clas

C++ Primer 学习笔记_75_模板与泛型编程 --模板定义

模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模板的定义. 泛型编程与面向对象编程一样,都依赖于某种形式的多态性.面向对象编程中的多态性在执行时应用于存在继承关系的类.我们能够编写使用这些类的代码,忽略基类与派生类之间类型上的差异.仅仅要使用基类的引用或指针,基类类型或派生类类型的对象就能够使用同样的代码. 在泛型编程中,我们所编写的类和函数能够

C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一些情况下,能够利用关于类型的一些特殊知识,编写比从模板实例化来的函数更有效率的函数. compare函数和 Queue类都是这一问题的好样例:与C风格字符串一起使用进,它们都不能正确工作. compare函数模板: template <typename Type> int compare(cons

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

模板与泛型编程 --一个泛型句柄类 引言: [小心地雷] 这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承和模板.在熟悉了这些特性之后再研究这个例子也许会帮助.另一方面,这个例子还能很好地测试你对这些特性的理解程度. 前面示例的Sales_item和Query两个类的使用计数的实现是相同的.这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数.原本不相关的Sales_item类型和 Query类型,可通过使用该模板进行公共的使用计数工作而得以简化.至于是公开还是隐藏下

C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]

模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还可以只特化push和pop成员.我们将特化push成员以复制字符数组,并且特化pop成员以释放该副本使用的内存: template<> void Queue<const char *>::push(const char *const &val) { char *new_item = new char[sizeof(val) + 1]; strncpy(new_item,val,sizeof(

C++ Primer 学习笔记_79_模板与泛型编程 --模板编译模型

模板与泛型编程 --模板编译模型 引言: 当编译器看到模板定义的时候,它不立即产生代码.只有在用到模板时,如果调用了函数模板或定义了模板的对象的时候,编译器才产生特定类型的模板实例. 一般而言,当调用函数时[不是模板],编译器只需看到函数的声明.类似的,定义类类型的对象时,类定义必须可用,但成员函数的定义不是必须存在的.因此,应该将类定义和函数声明放在头文件中,而普通函数和类成员函数的定义放在源文件中. 模板则不同:要进行实例化,编译器必须能够访问定义模板的源代码.当调用函数模板或类模板的成员函

C++ Primer 学习笔记_80_模板与泛型编程 --类模板成员

模板与泛型编程 --类模板成员 引言: 这一节我们介绍怎样实现前面提到的Queue模板类. 标准库将queue实现为其他容器之上的适配器.为了强调在使用低级数据结构中设计的编程要点,我们将Queue实现为链表.实际上,在我们的实现中使用标准库可能是个更好的决定!!-_-. 1.Queue的实现策略 如图所示,我们实现两个类: 1)QueueItem类表示Queue的链表中的节点,该类有两个数据成员item和next: a. item保存Queue中元素的值,它的类型随Queue的每个实例而变化: