【C++ Primer读书笔记】第16章 模板与泛型编程

定义模板

函数模板

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>,若TX&X&&,则remove_reference<T>::typeX

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_referencestring实例化
  • remove_reference<string>的type成员是string
  • move的返回类型是string&&

此调用实例化函数string&& move(string&& t)

static_cast不做转换,调用的结果就是传入的右值引用

传递给move一个左值

string s1("hi");
s2 = std::move(s1);
  • 推断出的T的类型为string&
  • remove_referencestring&实例化
  • remove_reference<string&>的type成员是string
  • move的返回类型仍是string&&

此调用实例化函数string&& move(string& t)

static_cast<string&&>(t)会把左值引用转换为右值引用(从一个左值static_cast到一个右值引用是允许的)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值