C++模版与泛型编程

C++模版与泛型编程

模版是C++泛型编程的基础,泛型编程所依赖的是编译时多态性或参数式多态性。

定义模版

函数模版

template <typename T>
int compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}

<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空

当我们调用一个函数模版时,编译器用函数实参来为我们推断模版实参。

当我们实例化出模版的一个特定版本时,编译器才会生成代码。

需要注意,当我们希望通知编译器一个名字表示类型时,必须使用关键字typename,而不能使用class。

template <typename T>
void test(const T& t)
{
    // 此时p被声明为指针,而不是X * X
    typename T::value_type * p;
}

类模版

template <typename T> class Blob
{
public:
    typedef T value_type;
    //std::vector<T>这个模板类中的size_type嵌套类型定义了一个叫做size type的别名。也可以使用typedef来引用实例化的类
    typedef typename std::vector<T>::size_type, size type;
    
    ...
        
}

函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须在程序中显式地指定。即函数模板允许隐式调用和显式调用而类模板只能显示调用。在使用时类模板必须加,而函数模板不必。

template <template T1, template T2, template T3>
T3 alternative_sum(T2, T1);

//错误:不能推断前几个模板参数
auto val3 = alternative_sum<long long>(i, lng);
//正确:显示地指定了所有三个参数
auto val2 = alternative_sum<long long, int, long>(i, lng);

对于上述模板类型参数已经显示指定了得函数实参,可以进行正常的类型转换。

// 1和2被转换成long
alternative_sum<long, long, long long>(1, 2);

友元与类模板

如果一个类模板包含一个非模板友元,则友元被授权可以访问所以模板实例。如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例。

template <typename T> class testfriend;

template <typename T>
class test
{
    friend class testfriend<T>;
private:
    int m_count;
};

template <typename T>
class testfriend
{
public:
    int get_count(const test<T>& t) { return t.m_count; }
};

test<int> t;
testfriend<int> tf;
// 正确
tf.get_count(t);

testfriend<double> tf1;
// 错误,必须相同类型
tf1.get_count(t);

类模板的static成员

与任何其他类相同,类模板也可以声明static成员

template <typename T>
class Foo
{
public:
    static std::size_t count() {return ctr;}
private:
    static std::size_t ctr;
};

// 以下两个是不同的静态对象
Foo<string> fs;
Foo<int> fi;
// 对象共享
Foo<int> fi2, fi3;

模版实参推断和引用

当一个函数参数是模板类型参数的一个普通(左值)引用时(T&),绑定规则告诉我们,只能传递给它一个左值(如,一个变量或一个返回引用类型的表达式)。实参可以是const类型,则T将被推断为const类型。如果实参类型是const T&,则实参可以是右值,T的类型推断结果不会是一个const类型。

// 	实参必须是一个左值
template <typename T>
void f1(T& t)
{
}

// 可以接受右值
template <typename T>
void f2(const T& t)
{
}

int i = 0;
const int j = 0;

f1(i);    // 正确
f1(5);    // 错误
f1(j);    // 正确,T是const int

f2(i);    // 正确
f2(5);    // 正确
f2(j);    // 正确,T是int

注意下面这个例子,

template <typename T>
void test(T&& t)
{
}

test(1);    // 正确 T是int

int i = 0;
test(i);    // 看起来i是左值,好像不合法,但是T被推断为int&,其实test被折叠成左值引用

引用折叠规则:
T& &(引用的引用)、T& &&(右值引用的引用)、T&& &(引用的右值引用)均折叠为T &。
T&& &&(右值引用的右值引用)折叠为T &&。

上面的类型别名和函数模板均触发了引用折叠。引用折叠是std::move、std::forward等的工作基础。

未完待续

多重定义?

何时必须使用typedefine?

理解std::move、std::forward,理解右值引用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值