目录
1、函数与宏
宏代码块
优点:代码复用,适合所有的类型 。缺点:编译器不知道宏的存在,缺少类型检查
函数
优点:真正的函数调用,编译器对类型进行检查
缺点:根据类型重复定义函数,无法代码复用
#include <iostream>
using namespace std;
#define SWAP(t, a, b) \
do \
{ \
t c = a; \
a = b; \
b = c; \
}while(0)
void Swap(int& a, int& b)
{
int c = a;
a = b;
b = c;
}
void Swap(double& a, double& b)
{
double c = a;
a = b;
b = c;
}
void Swap(string& a, string& b)
{
string c = a;
a = b;
b = c;
}
int main()
{
int a = 0;
int b = 1;
Swap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
double m = 2;
double n = 3;
Swap(m, n);
cout << "m = " << m << endl;
cout << "n = " << n << endl;
string d = "Nyist";
string t = "Wss";
Swap(d, t);
cout << "d = " << d << endl;
cout << "t = " << t << endl;
return 0;
}
泛型编程的概念 :不考虑具体数据类型的编程方式
2、函数模板
C++中的函数模板 :—种特殊的函数可用不同类型进行调用 ,看起来和普通函数很相似,区别是类型可被参数化
使用函数模板时:可以自动类型推导调用 ,也可以具体类型显示调用
本质:模板参数是在编译阶段被处理的单元,因此,在编译阶段必须准确无误的唯—确
#include <iostream>
using namespace std;
// template关键字用于声明开始进行泛型编程
// typename关键字用于声明泛指类型
template <typename T>
void Swap(T& a, T& b) // Swap泛型写法中的T不是—个具体的数据类型,而是泛指任意的数据类型。
{
T t = a;
a = b;
b = t;
}
template < typename T >
void Sort(T a[], int len)
{
for(int i=0; i<len; i++)
{
for(int j=i; j<len; j++)
{
if( a[i] > a[j] ) // comparable
{
Swap(a[i], a[j]);
}
}
}
}
template < typename T >
void Println(T a[], int len)
{
for(int i=0; i<len; i++)
{
cout << a[i] << ", ";
}
cout << endl;
}
template < typename T, int N > // N不能接收变量
void func()
{
T a[N] = {0};// 使用模板参数定义局部数组
for(int i=0; i<N; i++)
{
a[i] = i;
}
for(int i=0; i<N; i++)
{
cout << a[i] << endl;
}
}
int main()
{
int a = 0;
int b = 1;
Swap(a, b); // 自动推导
float c = 2;
float d = 3;
Swap<float>(c, d); // 显式调用
func<double, 10>();
int arr[5] = {5, 3, 2, 4, 1};
Println(arr, 5);
Sort(arr, 5);
Println(arr, 5);
string s[5] = {"Java", "C++", "Pascal", "Ruby", "Basic"};
Println(s, 5);
Sort(s, 5);
Println(s, 5);
return 0;
}
3、模板的两次编译
编译器从函数模板通过具体类型产生不同的函数 ,编译器会对函数模板进行两次编译:对模板代码本身进行编译 ,对参数替换后的代码进行编译
函数模板本身不允许隐式类型转换 ,自动推导类型时,必须严格匹配 ;显式类型指定时,能够进行隐式类型转换
#include <iostream>
using namespace std;
class Test
{
Test(const Test&); // private,不能拷贝构造
public:
Test(){}
};
template < typename T >
void Swap(T& a, T& b) // 先会对模板代码本身进行编译,确保无语法错误,替换参数类型后再次编译,确保再次无语法错误
{
T c = a; // T如果是Test就会编译报错,因为Test拷贝构造函数访问权限为private
a = b;
b = c;
}
typedef void(FuncI)(int&, int&);
typedef void(FuncD)(double&, double&);
typedef void(FuncT)(Test&, Test&);
int main()
{
FuncI* pi = Swap; // 编译器自动推导 T 为 int
FuncD* pd = Swap; // 编译器自动推导 T 为 double
//FuncT* pt = Swap; // 编译器自动推导 T 为 Test ,编译出错,说明二次编译
cout << "pi = " << reinterpret_cast<void*>(pi) << endl;
cout << "pd = " << reinterpret_cast<void*>(pd) << endl;
//cout << "pt = " << reinterpret_cast<void*>(pt) << endl;
return 0;
}
4、多参数函数模板
函数模板可以定义任意多个不同的类型参数,对于多参数函数模板 无法自动推导返回值类型 ,
可以从左向右部分指定类型参数 ,工程中将返回值参数作为第一个类型的参数
#include <iostream>
using namespace std;
template < typename T1, typename T2>
void Print(T1 a, T2 b)
{
cout << a << "\t" << b << endl;
}
template < typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
return static_cast<T1>(a + b);
}
int main()
{
Print(1, "wss");
// T1 = int, T2 = double, T3 = double
int r1 = Add<int>(0.5, 0.8); // 由于无法自动推导返回值类型,可以从左向右部分指定类型参数
// T1 = double, T2 = float, T3 = double
double r2 = Add<double, float>(0.5, 0.8);
// T1 = float, T2 = float, T3 = float
float r3 = Add<float, float, float>(0.5, 0.8);
cout << "r1 = " << r1 << endl; // r1 = 1
cout << "r2 = " << r2 << endl; // r2 = 1.3
cout << "r3 = " << r3 << endl; // r3 = 1.3
return 0;
}
5、重载函数模板
函数模板可以像普通函数—样被重载
C++编译器优先考虑普通函数 ,如果函数模板可以产生—个更好的匹配,那么选择模板
可以通过空模板实参列表限定编译器只匹配模板
#include <iostream>
using namespace std;
template < typename T >
T Max(T a, T b)
{
cout << "T Max(T a, T b)" << endl;
return a > b ? a : b;
}
int Max(int a, int b)
{
cout << "int Max(int a, int b)" << endl;
return a > b ? a : b;
}
template < typename T >
T Max(T a, T b, T c)
{
cout << "T Max(T a, T b, T c)" << endl;
return Max(Max(a, b), c);
}
int main()
{
int a = 1;
int b = 2;
cout << Max(a, b) << endl; // 普通函数 Max(int, int)
cout << Max<>(a, b) << endl; // 函数模板 Max<int>(int, int)
cout << Max(3.0, 4.0) << endl; // 函数模板 Max<double>(double, double)
cout << Max(5.0, 6.0, 7.0) << endl; // 函数模板 Max<double>(double, double, double)
cout << Max('a', 100) << endl; // 普通函数 Max(int, int)
return 0;
}
函数模板能够根据实参对参数类型进行推导 ,函数模板支持显示的指定参数类型
函数模板通过具体类型产生不同的函数 ,函数模板可以定义任意多个不同的类型参数
函数模板可以像普通函数—样被重载
6、C++中的类模板
类模板
一些类主要用于存储和组织数据元素 ,类中数据组织的方式和数据元素的具体类型无关 。如:数组类,链表类,Stack类,Queue类,等
C++中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。
C++中的类模板
以相同的方式处理不同的类型 ,在类声明前使用template进行标识 ,< typename T >用于说明类中使用的泛指类型T
声明的泛指类型T可以出现在类模板的任意地方 ,编译器对类模板的处理方式和函数模板相同
☛ 从类模板通过具体类型产生不同的类
☛ 在声明的地方对类模板代码本身进行编译
☛ 在使用的地方对参数替换后的代码进行编译
类模板的使用
只能显式指定具体类型,无法自动推导 ,使用具体类型 <Type> 定义对象 → Operator<int> op1;
#include <iostream>
#include <string>
using namespace std;
string operator - (string& l, string& r)
{
return "Minus";
}
template < typename T >
class Operator
{
public:
T add(T a, T b)
{
return a + b;
}
T minus(T a, T b)
{
return a - b;
}
T multiply(T a, T b)
{
return a * b;
}
T divide(T a, T b)
{
return a / b;
}
};
int main()
{
Operator<int> op1;
cout << op1.add(1, 2) << endl;
Operator<string> op2; // 必须重载-操作符 ,否则第二次编译错误
cout << op2.add("a", "b") << endl;
cout << op2.minus("a", "b") << endl;
return 0;
}
类模板的工程应用:类模板必须在头文件中定义 ,类模板不能分开实现在不同的文件中 ,类模板外部定义的成员函数需要加上模板<>声明
#ifndef _OPERATOR_H_
#define _OPERATOR_H_
template < typename T >
class Operator
{
public:
T add(T a, T b);
T minus(T a, T b);
T multiply(T a, T b);
T divide(T a, T b);
};
template < typename T >
T Operator<T>::add(T a, T b)
{
return a + b;
}
template < typename T >
T Operator<T>::minus(T a, T b)
{
return a - b;
}
template < typename T >
T Operator<T>::multiply(T a, T b)
{
return a * b;
}
template < typename T >
T Operator<T>::divide(T a, T b)
{
return a / b;
}
#endif
#include <iostream>
#include "Operator.h"
using namespace std;
int main()
{
Operator<int> op1;
cout << op1.add(1, 2) << endl;
cout << op1.multiply(4, 5) << endl;
cout << op1.minus(5, 6) << endl;
cout << op1.divide(10, 5) << endl;
return 0;
}
7、模板的特化
类模板可以定义任意多个不同的类型参数
类模板可以被特化
-指定类模板的特定实现 ,部分类型参数必须显式指定 ,根据类型参数分开实现类模板
-特化只是模板的分开实现 ,本质上是同—个类模板 ;特化类模板的使用方式是统—的,必须显式指定每—个类型参数
类模板的特化类型
-部分特化-用特定规则约束类型参数
-完全特化-完全显式指定类型参数
#include <iostream>
using namespace std;
template <typename T1, typename T2>
class Test{
public:
Test(){cout << "template <typename T1, typename T2>" << endl;}
};
// 部分特化
template <typename T>
class Test<T, T>{
public:
Test(){cout << "Test<T, T>" << endl;}
};
// 部分特化
template <typename T>
class Test<T, int>{
public:
Test(){cout << "Test<T, int>" << endl;}
};
// 完全特化
template <>
class Test<int, int>{
public:
Test(){cout << "Test<int, int>" << endl;}
};
int main()
{
Test<double, short> t0;
Test<double, double> t1;
Test<double, int> t2;
Test<int, int> t3;
return 0;
}
#include <iostream>
using namespace std;
template <typename T1, typename T2>
class Test
{
public:
void add(T1 a, T2 b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << a + b << endl;
}
};
// 关于指针的特化实现
template <typename T1, typename T2>
class Test <T1*, T2*>
{
public:
void add(T1* a, T2* b)
{
cout << "void add(T1* a, T2* b)" << endl;
cout << *a + *b << endl;
}
};
// 当 Test 类模板的两个类型参数完全相同时,使用这个实现
template <typename T>
class Test <T, T>
{
public:
void add(T a, T b)
{
cout << "void add(T a, T b)" << endl;
cout << a + b << endl;
}
void print()
{
cout << "class Test < T, T >" << endl;
}
};
// 当 T1 == void* 并且 T2 == void* 时 ,完全特化
template <>
class Test < void*, void* >
{
public:
void add(void* a, void* b)
{
cout << "void add(void* a, void* b)" << endl;
cout << "Error to add void* param..." << endl;
}
};
int main()
{
Test<int, float> t1;
Test<long, long> t2;
Test<void*, void*> t3;
t1.add(1, 2.5);
t2.add(5, 5);
t2.print();
t3.add(NULL, NULL);
Test<int*, double*> t4;
int a = 1;
double b = 0.1;
t4.add(&a, &b);
return 0;
}
重定义和特化的不同
重定义 : —个类模板和—个新类(或者两个类模板) ,使用的时候需要考虑如何选择的问题
特化 :以统—的方式使用类模板和特化类 ,编译器自动优先选择特化类
函数模板的特化: 函数模板只支待类型参数完全特化
工程中的建议:当需要重载函数模板时,优先考虑使用模板特化 ,当模板特化无法满足需求,再使用函数重载!
template < typename T > // 函数模板定义
bool Equal(T a, T b)
{
return a == b;
}
template < > // 函数模板完全特化
bool Equal<void*>(void* a, void* b)
{
return a == b;
}
#include <iostream>
using namespace std;
template < typename T1, typename T2 >
class Test
{
public:
void add(T1 a, T2 b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << a + b << endl;
}
};
// 重定义
class Test_Void
{
public:
void add(void* a, void* b)
{
cout << "void add(void* a, void* b)" << endl;
cout << "Error to add void* param..." << endl;
}
};
template < typename T >
bool Equal(T a, T b)
{
cout << "bool Equal(T a, T b)" << endl;
return a == b; // 两个浮点数不能直接比较
}
template < > // 函数模板的完全特化
bool Equal<double>(double a, double b)
{
const double delta = 0.00000000000001;
double r = a - b;
cout << "bool Equal<double>(double a, double b)" << endl;
return (-delta < r) && (r < delta);
}
bool Equal(double a, double b) // 重载
{
const double delta = 0.00000000000001;
double r = a - b;
cout << "bool Equal(double a, double b)" << endl;
return (-delta < r) && (r < delta);
}
int main()
{
cout << Equal( 1, 1 ) << endl; // bool Equal(T a, T b)
cout << Equal<>( 0.001, 0.001 ) << endl; // <>使用模板,否则会优先选择重载函数
return 0;
}
有趣的面试题
用最高效的方法求 1 + 2 + 3 + … + N 的值!
#include <iostream>
using namespace std;
template
< int N >
class Sum
{
public:
static const int VALUE = Sum<N-1>::VALUE + N;
};
template < >
class Sum < 1 >
{
public:
static const int VALUE = 1; // VALUE进入符号表,且会分配空间在全局数据区
};
// 先编译发现语法无误,带入参数10后再次编译,即编译期就可以确定VALUE的值
int main()
{
cout << "1 + 2 + 3 + ... + 10 = " << Sum<10>::VALUE << endl; // 仅仅访问常量值,编译阶段已经得到结果
cout << "1 + 2 + 3 + ... + 100 = " << Sum<100>::VALUE << endl;
//cout << sizeof(Sum<10>) << endl; // 1 ,可以说Sum是空类
return 0;
}
8、继承模板类访问基类成员
继承模板类要使用this指针或Base::访问基类成员
本质就是二次编译
第一阶段编译时对模板代码本身编译,确定语法等无误。此时子类继承父类,父类也是第一阶编译还未成型被忽略,
此时子类使用父类成员时必须显式指明。如:使用this表明变量就是本对象里有的 或 加上作用域指明父类有这个变量
第二阶段编译类型参数被替换,父子关系模型确认
#include <iostream>
using namespace std;
template <typename T>
class Base
{
protected:
T* m_array;
int length;
public:
void print()
{
cout << "OK" << endl;
}
};
template <typename T, int N>
class Derived : public Base<T>
{
T m_space[N];
public:
Derived()
{
this->Base<T>::m_array = m_space; // this->m_array = m_space;
this->length = 0; // Base<T>::m_length = 0;
}
};
int main()
{
Derived<int, 5> d;
d.print(); // OK
return 0;
}
令人郁闷的是在VS2015中测试,不加this也可以正常使用。。。但绝不编写依赖编译器的代码。。。
9、typename与class
为什么模板中声明泛指类型有的使用typename,有的使用class?
如下面代码使用typename与class没有区别
#include <iostream>
using namespace std;
template < class T > // template < typename T >
class Test
{
public:
Test(T t)
{
cout << "t = " << t << endl;
}
};
template < class T >
void func(T a[], int len)
{
for(int i=0; i<len; i++)
{
cout << a[i] << endl;
}
}
int main()
{
Test<string> ts("aaa");
string ta[]={"W","W","W","W"};
func(ta, 4);
Test<int> ti(100);
int ai[]={1, 2, 3, 4};
func(ai, 4);
return 0;
}
历史上的原因
-早期的C++直接复用class关键字来定义模板 ,但是泛型编程针对的不只是类类型 ,class关键字的复用使得代码出现二义性
typename诞生的直接诱因
-自定义类类型内部的嵌套类型 ,不同类中的同一个标识符可能导致二义性 ,编译器无法辨识标识符究竟是什么
-typename可以消除模板中的二义性
#include <iostream>
using namespace std;
int a = 0;
class Test_1
{
public:
static const int TS = 1;
};
class Test_2
{
public:
struct TS
{
int value;
};
};
template
< class T >
void test_class()
{ // 两种理解方式 - 二义性
T::TS * a; // 1. 通过泛指类型 T 内部的数据类型 TS 定义指针变量 a (推荐的解读方式)
// 2. 使用泛指类型 T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作
}
int main(int argc, char *argv[])
{
test_class<Test_1>();
test_class<Test_2>(); // error: missing 'typename' prior to dependent type name 'Test_2::TS'
// 编译器默认认为Test_2::TS是一个静态成员变量而不是数据类型
// 解决方案: typename T::TS * a;
// typename的两个作用:1. 在模板定义中声明泛指类型 2. 明确告诉编译器其后的标识符为类型
// 所以此时test_class<Test_1>();会编译出错
return 0;
}
10、数组类模板
静态数组类模板
#ifndef _ARRAY_H_
#define _ARRAY_H_
template < typename T, int N >
class Array
{
T m_array[N];
public:
int length();
bool set(int index, T value);
bool get(int index, T& value);
T& operator[] (int index);
T operator[] (int index) const;
virtual ~Array();
};
template < typename T, int N >
int Array<T, N>::length()
{
return N;
}
template < typename T, int N >
bool Array<T, N>::set(int index, T value)
{
bool ret = (0 <= index) && (index < N);
if( ret )
{
m_array[index] = value;
}
return ret;
}
template < typename T, int N >
bool Array<T, N>::get(int index, T& value)
{
bool ret = (0 <= index) && (index < N);
if( ret )
{
value = m_array[index];
}
return ret;
}
template < typename T, int N >
T& Array<T, N>::operator[] (int index)
{
return m_array[index];
}
template < typename T, int N >
T Array<T, N>::operator[] (int index) const
{
return m_array[index];
}
template < typename T, int N >
Array<T, N>::~Array()
{
}
#endif
堆数组模板类
#ifndef _HEAPARRAY_H_
#define _HEAPARRAY_H_
template
< typename T >
class HeapArray
{
private:
int m_length;
T* m_pointer;
HeapArray(int len);
HeapArray(const HeapArray<T>& obj);
bool construct();
public:
static HeapArray<T>* NewInstance(int length);
int length();
bool get(int index, T& value);
bool set(int index ,T value);
T& operator [] (int index);
T operator [] (int index) const;
HeapArray<T>& self();
~HeapArray();
};
template
< typename T >
HeapArray<T>::HeapArray(int len)
{
m_length = len;
}
template
< typename T >
bool HeapArray<T>::construct()
{
m_pointer = new T[m_length];
return m_pointer != NULL;
}
template
< typename T >
HeapArray<T>* HeapArray<T>::NewInstance(int length)
{
HeapArray<T>* ret = new HeapArray<T>(length);
if( !(ret && ret->construct()) )
{
delete ret;
ret = 0;
}
return ret;
}
template
< typename T >
int HeapArray<T>::length()
{
return m_length;
}
template
< typename T >
bool HeapArray<T>::get(int index, T& value)
{
bool ret = (0 <= index) && (index < length());
if( ret )
{
value = m_pointer[index];
}
return ret;
}
template
< typename T >
bool HeapArray<T>::set(int index, T value)
{
bool ret = (0 <= index) && (index < length());
if( ret )
{
m_pointer[index] = value;
}
return ret;
}
template
< typename T >
T& HeapArray<T>::operator [] (int index)
{
return m_pointer[index];
}
template
< typename T >
T HeapArray<T>::operator [] (int index) const
{
return m_pointer[index];
}
template
< typename T >
HeapArray<T>& HeapArray<T>::self()
{
return *this;
}
template
< typename T >
HeapArray<T>::~HeapArray()
{
delete[]m_pointer;
}
#endif
main.cpp
#include <iostream>
#include <string>
#include "Array.h"
#include "HeapArray.h"
using namespace std;
int main()
{
Array<double, 5> ad;
for(int i=0; i<ad.length(); i++)
{
ad[i] = i * i;
}
for(int i=0; i<ad.length(); i++)
{
cout << ad[i] << endl;
}
cout << endl;
HeapArray<char>* pai = HeapArray<char>::NewInstance(10);
if( pai != NULL )
{
HeapArray<char>& ai = pai->self();
for(int i=0; i<ai.length(); i++)
{
ai[i] = i + 'a';
}
for(int i=0; i<ai.length(); i++)
{
cout << ai[i] << endl;
}
}
delete pai;
return 0;
}
11、单例类模板
定义—个类,使得这个类最多只能创建—个对象
要控制类的对象数目,必须对外隐藏构造函数,将构造函数的访问属性设置为private
定义instance并初始化为NULL ,当需要使用对象时,访问instance的值
- 如果是空值:创建对象,并用instance标记
- 如果是非空值:返回instance标记的对象
#include <iostream>
using namespace std;
class SObject
{
static SObject* c_instance;
SObject(const SObject&);
SObject& operator= (const SObject&);
SObject()
{
//隐藏构造函数,达到无法在外部创建对象的目的
}
public:
static SObject* GetInstance(); //提供一个接口创建和访问唯一的对象
void print()
{
cout << "this = " << this << endl;
}
};
SObject* SObject::c_instance = NULL;
SObject* SObject::GetInstance()
{
if( c_instance == NULL )
{
c_instance = new SObject();
}
return c_instance;
}
int main()
{
SObject* s = SObject::GetInstance();
SObject* s1 = SObject::GetInstance();
SObject* s2 = SObject::GetInstance();
s->print();
s1->print();
s2->print();
return 0;
}
存在的问题 :需要使用单例模式时必须定义静态成员变量c_instance ,必须定义静态成员函数Getlnstance()
解决方案 :将单例模式相关的代码抽取出来,开发单例类 模板。当需要单例类时,直接使用单例类模板
#ifndef _SINGLETON_H_
#define _SINGLETON_H_
template
< typename T >
class Singleton
{
static T* c_instance;
public:
static T* GetInstance();
};
template
< typename T >
T* Singleton<T>::c_instance = NULL;
template
< typename T >
T* Singleton<T>::GetInstance()
{
if( c_instance == NULL )
{
c_instance = new T();
}
return c_instance;
}
#endif
#include <iostream>
#include "Singleton.h"
using namespace std;
class SObject
{
friend class Singleton<SObject>; // 当前类需要使用单例模式
SObject(const SObject&);
SObject& operator= (const SObject&);
SObject()
{
}
public:
void print()
{
cout << "this = " << this << endl;
}
};
int main()
{
SObject* s = Singleton<SObject>::GetInstance();
SObject* s1 = Singleton<SObject>::GetInstance();
SObject* s2 = Singleton<SObject>::GetInstance();
s->print();
s1->print();
s2->print();
return 0;
}
12、判断变量是不是指针
C++中仍然支持C语言中的可变参数函数 ,C++编译器的匹配调用优先级: 1. 重载函数 2. 函数模板 3. 变参函数
指针的判别
将变量分为两类:指针 vs 非指针
利用函数模板和变参函数能够判断指针变量
编写函数:指针变量调用时返回true ,非指针变量调用时返回false
// 函数模板与变参函数的化学反应
template
<typename T>
bool IsPtr(T* v) // match pointer
{
return true;
}
bool IsPtr(...) // match non-pointer
{
return false;
}
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
}
virtual ~Test()
{
}
};
template
<typename T>
bool IsPtr(T* v) // match pointer
{
return true;
}
bool IsPtr(...) // match non-pointer
{
return false;
}
int main(int argc, char *argv[])
{
int i = 0;
int* p = &i;
cout << "p is a pointer: " << IsPtr(p) << endl; // 1
cout << "i is a pointer: " << IsPtr(i) << endl; // 0
Test t;
Test* pt = &t;
cout << "pt is a pointer: " << IsPtr(pt) << endl; // 1
/*
* error: cannot pass object of non-trivial type 'Test' through variadic function; call will abort at runtime
*/
cout << "t is a pointer: " << IsPtr(t) << endl; // 0 或 程序崩溃
return 0;
}
存在的缺陷: 变参函数无法解析对象参数,可能造成程序崩溃!
进—步的挑战: 如何让编译器精确匹配函数,但不进行实际的调用?
完美解决方案:
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
}
virtual ~Test()
{
}
};
template
<typename T>
char IsPtr(T* v) // match pointer
{
return 'd';
}
int IsPtr(...) // match non-pointer
{
return 0;
}
#define ISPTR(p) (sizeof(IsPtr(p)) == sizeof(char)) // 通过使用sizeof在编译期间就能确定ISPTR(p)的值
int main(int argc, char *argv[])
{
int i = 0;
int* p = &i;
cout << "p is a pointer: " << ISPTR(p) << endl; // true
cout << "i is a pointer: " << ISPTR(i) << endl; // false
Test t;
Test* pt = &t;
cout << "pt is a pointer: " << ISPTR(pt) << endl; // true
cout << "t is a pointer: " << ISPTR(t) << endl; // false
return 0;
}