文章目录
定义模板
函数模板
template <typename T>
int compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
compare(1, 0);
在调用compare时,编译器根据实参的类型推断出模板参数,实例化一个特定版本的函数
类模板
与函数模板不同,编译器不能为类模板推断模板参数类型,需要显式指定
vector<string> v1;
vector<int> v2;
一个类模板的每个实例都形成一个独立的类,每个类之间相互不影响
类模板的static成员
template <typename T> class Foo
{
public:
static std::size_t count() { return ctr; }
private
static std::size_t ctr;
}
auto ct = Foo<int>::count();
Foo的每个实例拥有其自己的static成员实例,所有Foo<X>类型的对象共享相同的ctr和count
成员函数只有在使用时才会实例化
模板参数
使用类的类型成员
T::size_type * p;
需要明确是定义一个名为p的变量,还是将一个名为size_type的static数据成员与名为p的变量相乘
默认通过作用域运算符访问的名字不是类型,使用typename显式表示该名字是一个类型
typename T::size_type * p;
模板实参推断
类型转换与模板类型参数
能够应用于函数模板参数的类型转换:
-
const转换:可以将一个非const对象的引用或指针传递给一个const的引用或指针形参
-
数组或函数指针转换:如果函数形参不是引用类型,则可以对数组或函数类型的实参应用指针转换。数组实参可以转换为指向其首元素的指针,函数实参可以转换为该函数类型的指针
模板实参推断和引用
从左值引用函数参数推断类型
template <typename T> void f1(T&); // 实参必须是一个左值
// 对f1的调用使用实参所引用的类型作为模板参数类型
f1(i); // i是int,模板参数类型T是int
f1(ci); // ci是const int,T是const int
f1(5); // 错误,传递给T&的实参必须是左值
const参数
template <typename T> void f2(const T&); // 可以接受一个右值
// 在每个调用中,f2的函数参数都被推断为const int&
f2(i); // i是int,模板参数类型T是int
f2(ci); // ci是const int,T是int
f2(5); // const&参数可以绑定到右值,T是int
从右值引用函数参数推断类型
template <typename T> void f3(T&&);
f3(42); // 实参是int类型的右值,T是int
f3(i) // 例外1,T为int&,函数参数折叠为int&
引用折叠和右值引用参数
两个例外规则
-
当把一个左值(如i)传递给函数的右值引用参数,且此右值引用指向模板类型参数(如T&&)时,编译器推断模板类型参数为实参的左值引用类型。例如调用
f3(i)
时,推断T的类型为int&。 -
如果间接创建一个引用的引用,会发生引用折叠
X& &
、X& &&
和X&& &
都折叠成类型X&
X&& &&
折叠成X&&
这两个规则导致:
-
如果一个函数参数是一个指向模板类型参数的右值引用(T&&),则它可以被绑定到一个左值,且:
-
如果实参是一个左值,则推断出的模板实参类型是一个左值引用,且函数参数将被实例化为一个左值引用参数(T&)
可以将任意类型的实参传递给T&&类型的函数参数
std::move
remove_reference
对remove_reference<T>
,若T
为X&
或X&&
,则remove_reference<T>::type
为X
std::move的定义
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
}
std::move是如何工作的
传递给move一个右值
string s2 = std::move(string("bye!"));
- 推断出的
T
的类型为string
remove_reference
用string
实例化remove_reference<string>
的type成员是string- move的返回类型是
string&&
此调用实例化函数string&& move(string&& t)
static_cast不做转换,调用的结果就是传入的右值引用
传递给move一个左值
string s1("hi");
s2 = std::move(s1);
- 推断出的
T
的类型为string&
remove_reference
用string&
实例化remove_reference<string&>
的type成员是string- move的返回类型仍是
string&&
此调用实例化函数string&& move(string& t)
static_cast<string&&>(t)
会把左值引用转换为右值引用(从一个左值static_cast到一个右值引用是允许的)