文章目录
历史的痕迹
你可能会看到类似下面的模板定义
#include <cstdlib>
#include <iostream>
using namespace std;
template<class T> //此处用class?
T Minus(T a, T b)
{
return a - b;
}
template<class T>
class Add
{
public:
T add(T a, T b)
{
return a + b;
}
};
int main(int argc, char *argv[])
{
cout<<Minus(3, 4)<<endl;
cout<<Minus<float>(0.3, 0.4)<<endl;
Add<double> ap;
cout<<ap.add(9, 8)<<endl;
cout<<ap.add(0.001, 0.1)<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
为什么class可以用来定义模板参数呢?
为什么还要引进typename呢?
①.小插曲:在类中可以定义其它的新类型
#include <cstdlib>
#include <iostream>
#include "smartpoint.hpp"
using namespace std;
//类某种程度上可以看作是命名空间
//所以类内部可以定义其他的数据类型
class Test{
public:
typedef int* PINT ;
struct Point{
int x;
int y;
};
class Sub{
public:
Sub(){
cout << "Sub()" <<endl;
}
void print(){
cout << "Hello World" <<endl;
}
};
};
int main(int argc, char *argv[])
{
Test::PINT p = new int(5);
Test::Point po = {2, 3};
Test::Sub sub;
cout << *p <<endl;
cout <<po.x << " " <<po.y <<endl;
sub.print();
delete p;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
②.在类模板中可以定义新的类型
#include <cstdlib>
#include <iostream>
#include "smartpoint.hpp"
using namespace std;
//在类模板中也可以 定义新的类型
template<class T, int N>
class Test{
public:
typedef T ElemType;
enum {
LEN = N
};
ElemType array[LEN];
};
int main(int argc, char *argv[])
{
Test<int, 5> t1;
Test<float, 3> t2;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
③.在函数模板中 使用类模板的内部类型
#include <cstdlib>
#include <iostream>
#include "smartpoint.hpp"
using namespace std;
//在类模板中也可以 定义新的类型
template<class T, int N>
class Test{
public:
typedef T ElemType;
enum {
LEN = N
};
ElemType array[LEN];
};
//函数模板
template<class T>
void test_copy(T& test, T::ElemType a[], int len) //只看 T::ElemType, 到底是 静态成员变量、还是 类中的类型
//此处有二义性
//编译器 默认为静态成员变量
//typename 告诉编译器 后面的T::ElemType 是一个类型
{
int l = len < T::LEN ? len : T::LEN; //数组:取较小的长度
for(int i=0; i<l; i++){
test.array[i] = a[i];
}
}
int main(int argc, char *argv[])
{
Test<int, 5> t1;
Test<float, 3> t2;
int ai[] = {5,4,3,2,1,0};
float af[] = {0.1, 0.2, 0.3};
test_copy(t1, ai, 6);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
④.改为typename
完整代码
#include <cstdlib>
#include <iostream>
#include "smartpoint.hpp"
using namespace std;
//在类模板中也可以 定义新的类型
template<typename T, int N> //class真的不合适
class Test{
public:
typedef T ElemType;
enum {
LEN = N
};
ElemType array[LEN];
};
//函数模板
template<typename T>
void test_copy(T& test, typename T::ElemType a[], int len) //只看 T::ElemType, 到底是 静态成员变量、 静态成员函数、还是 类中的类型
//此处有二义性
//编译器 默认为静态成员变量
{
int l = len < T::LEN ? len : T::LEN; //数组:取较小的长度
for(int i=0; i<l; i++){
test.array[i] = a[i];
}
}
int main(int argc, char *argv[])
{
Test<int, 5> t1;
Test<float, 3> t2;
int ai[] = {5,4,3,2,1,0};
float af[] = {0.1, 0.2, 0.3};
test_copy(t1, ai, 6);
for(int i=0; i<t1.LEN; i++){
cout << t1.array[i] <<endl;
}
cout << endl;
test_copy(t2, af, 6);
for(int i=0; i<t2.LEN; i++){
cout << t2.array[i] <<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
为什么class可以用来定义模板参数呢?
为什么还要引进typename呢?
模板最初的目标只是为了对类类型进行泛型操作的定义,因此用class关键字声明泛型类型
在之后的进化过程中发现了模板相互调用时产生的 :: 操作符的二义性
因此引入typename关键字是用于告诉编译器将 :: 符号后的标识符看作类型
坑爹的面试题
面试官:你的简历上写着你熟悉C++,那么你写函数判断一个变量是否为指针吗?
应聘者:。。。
小插曲
拾遗
C++中仍然支持C语言中的可变参数函数
C++编译器的匹配调用优先级
重载函数
函数模板
可变参数函数
①.C++编译器匹配示例
#include <cstdlib>
#include <iostream>
using namespace std;
//普通函数
int test(int i, int j)
{
cout<<"int test(int i, int j)"<<endl;
}
//函数模板
template<typename T>
T test(T i, T j)
{
cout<<"T test(T i, T j)"<<endl;
}
//可变参数 函数
int test(...)
{
cout<<"int test(...)"<<endl;
}
int main(int argc, char *argv[])
{
int i = 0;
int j = 0;
test(i, j);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
②.函数模板与 可变参数函数 的化学反应
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
void isPtr(T*)
{
cout << "void isPtr(T*)" <<endl;
}
void isPtr(...){
cout << "void isPtr(...)" <<endl;
}
int main(int argc, char *argv[])
{
int* pi = NULL;
float* pf = NULL;
int i = 0;
float f = 0.0f;
isPtr(pi);
isPtr(pf);
isPtr(i);
isPtr(f);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
①.解决方案一
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
bool isPtr(T*)
{
return true;
}
bool isPtr(...){
return false;
}
int main(int argc, char *argv[])
{
int* pi = NULL;
float* pf = NULL;
int i = 0;
float f = 0.0f;
cout << isPtr(pi) <<endl;
cout << isPtr(pf) <<endl;;
cout <<isPtr(i) <<endl;
cout <<isPtr(f)<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
面试官:你的方法实现了指针的判断,但是我觉得不够高效,你有更好的办法吗?
应聘者:。。。
分析:
解决方案1已经很好的解决面试官的问题,那么为什么还不够高效呢?哪里不够高效呢?
tips:
解决方案1中的唯一耗时的地方在于函数调用的建栈与退栈过程,因此需要考虑如何避免这个过程以提高程序效率。
②.解决方案二
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
char isPtr(T*);
int isPtr(...);
#define ISPTR(v) (sizeof(isPtr(v)) == sizeof(char) ) //不需要函数调用开销,比方案一 高效
int main(int argc, char *argv[])
{
int* pi = NULL;
float* pf = NULL;
int i = 0;
float f = 0.0f;
cout << ISPTR(pi) <<endl;
cout << ISPTR(pf) <<endl;;
cout << ISPTR(i) <<endl;
cout << ISPTR(f)<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}