关于模板更多的内容...

简介: 关于模板更多的内容...

特化和部分特化(部分特化也叫偏特化)

比如下面这个模板比较函数

template<typename T>
bool compare(T a, T b)
{
  return a > b;
}
int main()
{
  compare(10, 20);
  //实例化出const char*的比较函数,比较的是地址,而不是字典序
  compare("aaa", "bbb");
}

compare("aaa", "bbb");比较的是地址,而不是字典序,所以需要实现对字符串比较的特化版本:

template<typename T>
bool compare(T a, T b)
{
  cout << "template compare" << endl;
  return a > b;
}
template<>
bool compare<const char*>(const char*a, const char*b)
{
  cout << "compare<const char*>" << endl;
  return strcmp(a, b) > 0;
}
int main()
{
  compare(10, 20);
  compare("aaa", "bbb");
}

类模板特化:

template<typename T>
class Vector
{
public:
  Vector() { cout << "call Vector template init" << endl; }
};
//下面这个是对char*类型提供的完全特化版本
template<>
class Vector<char*>
{
public:
  Vector() { cout << "call Vector<char*> init" << endl; }
};
//下面这个是对指针类型提供的部分特化版本
//只知道是指针类型,不知道具体是什么类型
template<typename Ty>
class Vector<Ty*>
{
public:
  Vector() { cout << "call Vector<Ty*> init" << endl; }
};
//部分特化,针对有返回值,有两个形参变量的函数提供的函数指针
template<typename R, typename A1, typename A2>
class Vector<R(*)(A1, A2)>
{
public:
  Vector() { cout << "call Vector<R(*)(A1, A2)> init" << endl; }
};
//完全特化
//template<>
//class Vector<int(*)(int, int)>
//针对函数(有一个返回值,有两个形参变量)类型提供的部分特化
template<typename R, typename A1, typename A2>
class Vector<R(A1, A2)>
{
public:
  Vector() { cout << "call Vector<R(A1, A2)> init" << endl; }
};
int sum(int a, int b) { return a + b; }
int main()
{
  Vector<int> vec1;
  //优先使用完全特例化版本
  Vector<char*> vec2;
  //有部分特化就使用部分特化,没有的话就是用原模版
  Vector<int*> vec3;
  Vector<int(*)(int, int)> vec4;
  Vector<int(int, int)> vec5;
  //区分函数类型和函数指针类型
  typedef int(*PFUNC1)(int, int);
  PFUNC1 pfunc = sum;
  cout << pfunc(10, 20) << endl;
  typedef int PFUNC2(int, int);
  PFUNC2 *pfunc2 = sum;
  cout << (*pfunc2)(10, 20) << endl;
  return 0;
}

关于特化和部分特化(偏特化),其实就是看是否还有可推导的内容,特化实际上是为了应对特殊的场景。

模板的实参推演

int sum(int a, int b) { return a + b; }
template<typename T>
void func(T a)
{
  cout << typeid(T).name() << endl;
}
template<typename R, typename A1, typename A2>
void func2(R(*a)(A1, A2))
{
  cout << typeid(R).name() << endl;
  cout << typeid(A1).name() << endl;
  cout << typeid(A2).name() << endl;
}
class Test
{
public:
  int sum(int a, int b) { return a + b; };
};
template<typename R, typename T, typename A1, typename A2>
void func3(R(T::*a)(A1, A2))
{
  cout << typeid(R).name() << endl;
  cout << typeid(T).name() << endl;
  cout << typeid(A1).name() << endl;
  cout << typeid(A2).name() << endl;
}
int main()
{
  func(10);
  func("aaa");
  func(sum); //int (__cdecl*)(int,int)
  func2(sum);
  func(&Test::sum);
  func3(&Test::sum);
  return 0;
}

引用折叠

首先,&&在模板中并不是右值引用的意思,只是说明要传一个引用。

比如,现在有这样一个例子:

namespace gao {
template<typename T>
    void func(T &&a) {
    }
}
int main() {
  int n = 123;
    gao::func(n);
    gao::func(123);
  return 0;
}

我们分别注释掉两个调用,并用nm命令查看实例化出来的函数发现:

gao::func(n); -> 0000000000000000 W void gao::func<int&>(int&)
gao::func(123); -> 0000000000000000 W void gao::func<int>(int&&)

对于func(n),T类型被推导为int&

对于func(123),T类型被推导为int

按照我们开始说的"&&在模板中并不是右值引用的意思,只是说明要传一个引用"

n是左值,所以要对应左值引用,当T被推导为int&后,形参就变成了func(int& &&a)

同时,模板中奇数个&被标识为左值引用,偶数个&被标识为右值引用,所以三个&,引用折叠,最后函数形参就变为了左值引用。

123是右值,T被推导为int后,形参就变成了func(int &&)右值引用。

这时,如果想使用T类型定义变量时,也会报错:

namespace gao {
template<typename T>
    void func(T &&a) {
        T b;
    }
}

报错信息:

error: ‘b’ declared as reference but not initialized

因为,T被func(n)推导为int&类型,而int& b必须得有初始化值。

其实模板中提供了一种方法可以移除引用的类型:

namespace gao {
template<typename T>
    void func(T &&a) {
        typename remove_reference<T>::type b;
    }
}

这样,就不会报错了。

相关文章
|
2月前
|
算法 程序员 编译器
【C++】—— 模板介绍
【C++】—— 模板介绍
|
2月前
树链剖分模板
树链剖分模板
34 0
|
9月前
|
编译器 C语言 C++
C++:模板的相关内容
C++:模板的相关内容
|
3天前
|
存储 编译器 C++
【C++】详解C++的模板
【C++】详解C++的模板
|
2月前
|
存储 编译器 C++
|
2月前
|
Ubuntu Java Docker
Dockfile应用模板
Dockfile应用模板
76 0
|
2月前
|
C++
C++模板 - 模板的使用
C++模板 - 模板的使用
23 0
|
9月前
|
编译器 C++
【C++】初识模板
【C++】初识模板
24 0
|
编译器 C++
C++之模板(上)
C++之模板(上)
64 0
|
编译器 C++
【C++】模板
一下搞懂C++模板,学会函数模板和类模板
68 0

热门文章

最新文章

相关实验场景

更多