前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【C++】模板的改进

【C++】模板的改进

作者头像
利刃大大
发布2025-03-05 08:16:34
发布2025-03-05 08:16:34
4300
代码可运行
举报
文章被收录于专栏:csdn文章搬运csdn文章搬运
运行总次数:0
代码可运行

Ⅰ. 右尖括号的改进

​ 在 C++98/03 的泛型编程中,模板实例化有一个很繁琐的地方,就是连续两个右尖括号 >> 会被编译解释成右移操作符,而不是模板参数表的形式,需要一个空格进行分割,以避免发生编译时的错误。

代码语言:javascript
代码运行次数:0
复制
template<size_t i>
class X {};

template<class T>
class Y {};

int main()
{
    Y<X<1>> x1; // 在c++11中编译通过
    Y<X<2>> x2; // 在c++98中编译失败
    return 0;
}

​ 在实例化模板时会出现连续两个右尖括号,同样 static_castdynamic_castreinterpret_castconst_cast 表达式转换时也会遇到相同的情况。 C++98 标准是让程序员在 >> 之间填上一个空格,而在 C++11 中,这种限制被取消了!

C++11 标准中,要求编译器对模板的右尖括号做单独处理,使编译器能够正确判断出 >> 是一个右移操作符还是模板参数表的结束标记

Ⅱ. 模板别名 using

​ 模板别名其实就是就是利用模板和起别名两个特性结合起来!

​ 比如有以下场景,我们有一个 mapmap 里面放的第一个参数固定是 string 类型,但是第二个参数是未知的,而如果我们有好几个实例化对象都是不同类型的,那么在 c++98/03 的时候我们想给每个这种类型起别名的话,只能一个一个 typedef ,如下所示:

代码语言:javascript
代码运行次数:0
复制
typedef map<string, int> mint;
typedef map<string, float> mfloat;
// ......

​ 那么这样子就非常复杂,考虑到有大量重复的都是 string,而变化的只有第二个参数,我们可以利用模板:

代码语言:javascript
代码运行次数:0
复制
template <typename T>
typedef map<string, T> mt;

mt<int> mint;
mt<string> mstr;
// ......

​ 但是疑惑的是上述的定义不能通过编译!因为 c++98/03 并不支持这种语法,但是我们可以通过类来包装一下,照样能达到效果,如下所示:

代码语言:javascript
代码运行次数:0
复制
template <typename T> 
struct alias_mt
{
	typedef map<string, T> map;
}

alias_mt<int> mint;
alias_mt<string> mstr;
// ......

​ 通过包裹类的方法虽然可以实现上述的需求,但是一看代码就觉得,这个代码可读性差,不就是定义一个变量吗,还需要整一个包裹类来封装下?

​ 幸运的是 C++11 终于让我们可以不用通过上述这种臃肿的方式来实现这个需求了。C++11 中,新增了一个特性就是可以通过使用 using 来为一个模板定义别名,比如说上述的需求,使用 C++11 就可以这样实现:

代码语言:javascript
代码运行次数:0
复制
template <typename T>
using alias_mt = map<string, T>;
 
alias_mt<int>  mint;
alias_mt<string> mstr;

​ 实际上 using 包含了 typedef 的所有功能,来看下使用 using 关键字和 typedef 关键字定义普通类型别名的用法。

代码语言:javascript
代码运行次数:0
复制
#include<iostream>
#include<type_traits>

typedef int int32;    // 通过typedef 给一个类型起别名,不是新建类型
using my_int = int;   // c++11的方式

int main() 
{
    // is_same是判断两个类型是否一致,如果是则返回真,否则返回假
    std::cout << std::is_same<int32, my_int>::value << std::endl;
    return 0;
}

// 运行结果
1

​ 可以看到在对普通类型的别名定义上,两种方法的使用基本等效,唯一不同的仅仅是定义的语法,using 使用起来就像是赋值,但是在定义函数函数指针的时候,using 的可读性要好一点,比如下面的语句:

代码语言:javascript
代码运行次数:0
复制
typedef void(*func)(int, int);
using func = void(*)(int, int);

​ 可能突然看起来使用 using 的方式来定义一个函数指针有点怪,但是习惯了之后会发现使用 using 这种赋值的方式更适用开发人员的思考方式。下面再显示一个通过 typedefusing 方式分别来定义一个函数模板的例子:

代码语言:javascript
代码运行次数:0
复制
template <typename T>
struct FuncSt
{
	typedef void(*func)(T, T);
};
FuncSt<int>::func func_typedef;

-----------------------------------------------------------
    
template <typename T>
using func_using = void(*)(T, T);
func_using<int> func;

​ 可以看到通过 using 定义模板别名的语法,仅仅是在普通类型别名语法基础上增加了 template 参数列表,通过 using 可以轻松的创建一个模板的别名,而不需要像 C++98/03 那样增加一个包裹类。

但是需要额外注意的是使用 using 或者 typedef 仅仅是定义一个别名,不会创造新类型

Ⅲ. 函数模板的默认模板参数

​ 在 C++98/03 里,类模板是支持默认的模板参数的,比如:

代码语言:javascript
代码运行次数:0
复制
template <class T, class U = int, U v = 0>
struct test { 
    // ...... 
};

​ 但是在 C++98/03 中却 不支持函数模板的默认模板参数

代码语言:javascript
代码运行次数:0
复制
template <class T = int> // ❌不支持
void func(T x) { 
    // ...... 
}

​ 现在这个限制在 C++11 中已经解除,上述的定义在 C++11 中可以直接使用了。

​ 在函数模板中当所有模板参数都有默认参数时,函数的调用就如同普通的函数调用,但是对于类模板而言,哪怕所有模板参数都有默认构造函数在使用时还是必须在模板名后跟随 <> 来实例化

C++11 中函数的默认模板参数在使用规则上和其他的默认参数也有一些区别,普通函数的默认参数必须写在参数列表的最后,而函数的模板参数就没有这个限制,因此当使用默认模板参数和模板参数自动推导时就显示十分灵活,可以指定函数中的一部分参数是默认参数,另一部分采用自动推导。比如:

代码语言:javascript
代码运行次数:0
复制
template <typename T = int, typename U>
T func(U val)
{
	//...
}
 
int main()
{
	func(123);
	return 0;
}

​ 但是如果在使用函数模板时如果显示指定模板的参数,由于模板参数的填充顺序是自左向右的,因此像下面这样的调用返回的类型是 long 类型:

代码语言:javascript
代码运行次数:0
复制
func<long>(123);  // func返回类型是填充类型long

​ 这个细节虽然简单,但是在多个默认模板参数和多个模板参数自动推导穿插使用时会容易被忽略掉,造成使用上的一些意外,建议在使用的时候尽量还将默认模板参数写在模板参数的末尾

​ 另外当默认模板参数和自动参数推导同时使用时,若函数模板无法推导出参数类型时,编译器将使用默认模板参数,否则将使用自动推导的参数类型。这个跟函数的默认参数使用规则是一致的,比较好理解。

​ 模板别名以及默认模板参数是在泛型编程中的一些小细节,是 C++11C++98/03 一些细节上的提升,因此介绍的篇幅不多,主要是在使用的时候若可以的话可以通过这些小技巧增加代码可读性,减少代码冗余。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Ⅰ. 右尖括号的改进
  • Ⅱ. 模板别名 using
  • Ⅲ. 函数模板的默认模板参数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档