技术标签: traits C++的STL使用和源码学习
是一种数据类型公开的能力,利用typedef这样的语句实现类型萃取器
模板名<模板实参>::类型名 变量名
这样使用的目的:
在不同模板之间,在类的外部形成数据访问能力
示例代码:
#include <iostream>
using namespace std;
template<typename T>
struct test
{
typedef T value_type;
typedef T& reference;
typedef T* pointer;
};
int main()
{
test<int>::value_type a = 100.9;
cout << a << endl;
test<double>::value_type b = 100.9;
cout << b << endl;
}
上述代码内嵌了数据类型,从模板传入的类型衍生出了其对象相关类型的能力,外部的数据类型和内部传入的数据类型保持一致,保证了动态识别内部的数据类型
如果一个模板中,全部的成员都是public的,那么这个模板就是一个独立的数据类型表,用来规范数据
示例代码:
#include <iostream>
using namespace std;
//第一步:定义一个规范类模板类型表的基类模板
template<typename T,typename U>
class TypeTbl
{
public:
typedef T value_type1;
typedef T reference1;
typedef U value_type2;
typedef U reference2;
};
//第二步:凡是继承了这个类型表的模板,它的访问类型就被确定
template<typename T, typename U>
class Test :public TypeTbl<T, U>
{
};
int main()
{
Test<int, double>::value_type1 a = 200;
Test<int, double>::reference1 b = a;
cout << a << endl;
cout << b << endl;
}
在STL库中设计人员经常使用这样的技巧
Binary类中就使用了这样的技巧,对二元函数的形参类型进行了约束
示例代码:
#include <iostream>
using namespace std;
template <typename _A1,typename _A2,typename _R>
struct Binary
{
typedef _A1 Arg1;
typedef _A2 Arg2;
typedef _R Ret;
};
//设计一个继承了binary接口的类模板
template <typename TR, typename T1, typename T2>
class Add :public Binary<TR, T1, T2>
{
public:
TR bfun(const T1& x, const T2& y)const
{
return x + y;
}
};
int main()
{
double a = 100.09, b = 20.2;
Add<double, double, double> addObj;
cout << addObj.bfun(a,b)<< endl;
//使用Arg1定义一个变量x
typename Add<int, int, double>::Arg1 x = 1000;
cout << x << endl;
}
分析:
因为Add包含了Binary的数据类型表,因此系统中的其他模块就可以使用Add::Arg1,Add::Arg2,Add::Ret这种方式和Add本身进行对接。
这种数据类型的抽象,达到了多个系统模块之间的类型统一
示例代码:
class Test1
{
public:
char compute(int x, double y)
{
return x;
}
};
class Test2
{
public:
double compute(double x, double y)
{
return x;
}
};
//Test1和Test2都只有一个compute函数,而且函数逻辑也完全相同
//不同的仅仅是函数参数类型
//用模板来抽象Test1和Test2
template <typename Arg1,typename Arg2,typename Ret>
class Test
{
public:
Ret compute(Arg1 x, Arg2 y)
{
return x;
}
};
上述代码的分析:
没有很好的复用已经设计好的函数,在Test的设计中已经侵入了Test1和Test2的设计,能够把Test写出来,是直到Test1和Test2是怎么实现的,也就是要直到它传入的模板参数(比如给调用Test1版本的话传入<int,double,char>,而在调用Test2的时候传入<double,double,double>),而我们现在就不想知道它的实现,直接传入<Test1>和<Test2>来调用它们各自的版本的函数,写法如下:
#include <iostream>
using namespace std;
class Test1;
class Test2;
//两个类模板规范一个统一的接口
template <typename T>
class TypeTbl
{
};
//特化模板
template <>
class TypeTbl<Test1>
{
public:
typedef char ret_type;
typedef int par1_type;
typedef double par2_type;
};
template <>
class TypeTbl<Test2>
{
public:
typedef double ret_type;
typedef double par1_type;
typedef double par2_type;
};
template<typename T>
class Test
{
public:
typename TypeTbl<T>::ret_type compute(
typename TypeTbl<T>::par1_type x,
typename TypeTbl<T>::par2_type)
{
return x;
}
};
int main()
{
Test<Test1> t1;
cout << t1.compute(65,6.18) << endl;
}
这样的写法使得类数据类型再做了一次抽象
泛型——一个重要的特征就是约定代码中的复杂数据类型的基本特征
比如说指向Student类的对象的指针pStudent和指向Teacher类的对象的指针pTeacher,两个没有任何关系,这两个要相互转化,就需要借助void*,如void*pTemp=pStudent,pTemp=pTeacher,在这个过程中,pTemp是不含有pStudent和pTeacher的信息的,这样会出现类型不安全问题:指针天生不具备向外提供数据类型的能力。指针仅仅是一个4个字节存储着地址信息的变量,它没有办法约束类型
如果想要对类型约束,我们需要对指针进行模板特化,这样衍生出符合接口定义的统一类型型别的别名
示例代码:
#include <iostream>
using namespace std;
template <typename T>
class Iterator_1
{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
};
template <typename T>
class Iterator_2
{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
};
template<typename T>
struct Traits
{
};
template<typename T>
struct Traits<T*>
{
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
};
int main()
{
Iterator_1<int>::value_type t1= 100;
cout << t1 << endl;
Iterator_2<double>::value_type t2 = 100.5;
cout << t2 << endl;
Traits<double*>::value_type t3 = 100.09;
cout << t3 << endl;
}
这样的写法对类型进行了规范,用户传进去什么类型,这种别名传出来的就是什么类型,比如Iterator_1中传入了int,实际最后t1也为int,Traits中传入了double,t3就是double类型
通过Traits的数据类型信息,可以有效的规范出类型型别统一,从而避免了类型转化问题
Traits技术也是泛型的基石
文章浏览阅读1.1w次,点赞7次,收藏34次。vue-grid-layout的使用、实例、遇到的问题和解决方案_vue-grid-layout
文章浏览阅读218次。然后连接一个数据源,就会在下面自动产生一个添加附件的组件。把这个控件复制粘贴到页面里,就可以单独使用来上传了。插入一个“编辑”窗体。_powerapps点击按钮上传附件
文章浏览阅读264次。(1) Abstraction (抽象)(2) Polymorphism (多态)(3) Inheritance (继承)(4) Encapsulation (封装)_"object(cnofd[\"ofdrender\"])十条"
文章浏览阅读133次。删除node_modules,重新npm install看是否成功。在 package.json 文件中的 scripts 中加入。修改你的第三方库的bug等。然后目录会多出一个目录文件。_修改 node_modules
文章浏览阅读883次。【代码】【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure
文章浏览阅读1w次,点赞13次,收藏97次。整理5个优秀的微信小程序开源项目。收集了微信小程序开发过程中会使用到的资料、问题以及第三方组件库。_微信小程序开源模板
文章浏览阅读128次。Centos7最简搭建NFS服务器_centos7 搭建nfs server
文章浏览阅读1.2k次,点赞2次,收藏3次。前言mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。..._mybaitis-plus ruledataobjectattributemapper' and 'com.picc.rule.management.d
文章浏览阅读325次。EECE 1080C / Programming for ECESummer 2022Laboratory 4: Global Functions PracticePlagiarism will not be tolerated:Topics covered:function creation and call statements (emphasis on global functions)Objective:To practice program development b_eece1080c
文章浏览阅读53次。被同机房早就1年前就学过的东西我现在才学,wtcl。设要求的数为\(x\)。设当前处理到第\(k\)个同余式,设\(M = LCM ^ {k - 1} _ {i - 1}\) ,前\(k - 1\)个的通解就是\(x + i * M\)。那么其实第\(k\)个来说,其实就是求一个\(y\)使得\(x + y * M ≡ a_k(mod b_k)\)转化一下就是\(y * M ...
文章浏览阅读1.3k次。首先,问题是如何出现的?晚上复查代码,发现一个activity没有调用自己的ondestroy方法我表示非常的费解,于是我检查了下代码。发现再finish代码之后接了如下代码finish();System.exit(0);//这就是罪魁祸首为什么这样写会出现问题System.exit(0);////看一下函数的原型public static void exit (int code)//Added ..._android 手动杀死app,activity不执行ondestroy
文章浏览阅读894次。Q: SylixOS 版权是什么形式, 是否分为<开发版税>和<运行时版税>.A: SylixOS 是开源并免费的操作系统, 支持 BSD/GPL 协议(GPL 版本暂未确定). 没有任何的运行时版税. 您可以用她来做任何 您喜欢做的项目. 也可以修改 SylixOS 的源代码, 不需要支付任何费用. 当然笔者希望您可以将使用 SylixOS 开发的项目 (不需要开源)或对 SylixOS 源码的修改及时告知笔者.需要指出: SylixOS 本身仅是笔者用来提升自己水平而开发的_select函数 导致堆栈溢出 sylixos