泛型编程,最重要的点在于类型。包括类型之间的转换/类型推断/类型萃取/完美转发等

Templates 模板

  • 动机
void swap (int&, int&);
void swap (float&, float&);
void swap (double&, double&);
  • 函数模板
template <class T>
void swap (T& a, T& b){
    auto temp = std::move(a); //T temp = a;
    a = std::move(b); //a = b;
    b = std::move(temp); //b = temp;
}
  • 类模板
//<= c++14 types必须given explictly,c++17后才有类模板参数推断
template <class T>
struct point{
 T x;
 T y;
}

Type Casts 类型转换

  • Types Casts
    将类型名作为强制类型转换运算符的做法是c的老式做法,c++为保持兼容而予以保留,但不建议
static_cast<NewType>(expression)
dynamic_cast<NewType>(expression)
const_cast<NewType>(expression)
reinterpret_cast<NewType>(expression)
bit_cast<NewType>(expression) <c++20>
  • Custom Type Conversion
    1.via one-argu constructors -perfer this
    2.via conversion operatoes -be careful
class A{...}
class B{
public:
    explict B(A const&); //construct a B from an A
    explict operator A() const; //make an A from a b
    ...
}
1.static_cast
用于“比较自然”的和低风险的转换,非运行时转换,NewType必须要有能接受srcType的构造函数或者operator()转换函数才能work
- 使用场景:
a.用于基类和派生类之间指针或者引用的转换,上行安全,下行不安全
b.用于基本数据类型之间的转换,安全性问题由开发者来保证
c.把空指针转换成目标类型的空指针
d.把任何类型的表达式转为void类型
- 使用特点 :
a.主要执行<非>多态的转换操作,用于替代c中通常的转换操作
b.隐式转换都建议使用static_cast进行标明和替换

2.reinterpret_cast
从底层对数据进行重新解释,执行的是逐个比特复制的操作。
- 使用场景:
高危操作,旨在对数据进行重新解释

3.const_cast
const_cast 运算符<仅>用于进行去除 const属性的去除,它也是四个强制类型转换运算符中唯一能够去除 const 属性的运算符。是一个危险的动作
- 使用场景:
a.常量指针转换为非常量指针,并且仍然指向原来的对象
b.常量指针转换为非常量指针,并且仍然指向原来的对象

4.dynamic_cast
dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。运行时转换。
1.使用场景:
a.只有在派生类之间转换时才使用dynamic_cast。
2.使用特点:  
a.基类必须要有虚函数(基类析构函数必为虚函数),因为dynamic_cast是运行时类型检查,需要运行时类型信息,而这个信息是存储在类的虚函数表中,只有一个类定义了虚函数,才会有虚函数表(如果一个类没有虚函数,那么一般意义上,这个类的设计者也不想它成为一个基类)。  
b.对于下行转换,dynamic_cast是安全的(当类型不一致时,转换过来的是空指针),而static_cast是不安全的(当类型不一致时,转换过来的是错误意义的指针,可能造成踩内存,非法访问等各种问题)  
c.dynamic_cast还可以进行交叉转换
  • smart pointer cast (c++11)
auto sp = make_shared<X>();
static_pointer_cast<Y>(sp) <--> static_cast<Y*>(sp.get())
dynamic_pointer_cast<Y>(sp) <--> dynamic_cast<Y*>(sp.get())
const_pointer_cast<Y>(sp) <--> const_cast<Y*>(sp.get())

Type Deduction 类型推导

  • auto
int i = 1;
int& ri = i;
int const& cri = i;
// throws away cv-qualifiers and reference
auto x = i; //int
auto y = ri; //int
auto z = cri; //int
  • decltype
// preserves cv-qualifiers and reference
using T1 = decltype(i); //int
using T2 = decltype(ri) //int&
using T3 = decltyoe(cri) //int const&
  • decltype(auto) 当需要保留const或引用时使用(作为返回值)
// preserves cv-qualifiers and reference
decltype(auto)x = i; //int
decltype(auto)y = ri //int&
decltyoe(auto)z = cri //int const&

image.png

  • trail return types 感觉这么用更清晰
    image.png
  • std::declval

image.png

Type Traits 类型萃取

#include <type_traits>

Perfect Forwarding 完美转发

  • Motivation 动机 Generic Factory Functions
template<class T, calss A1, class A2, class A3>
auto factory(A1 a1 = A1(), A2 a2 = A1(), A3 a3 = A1()){ //看着有点别捏 ,A1(),A2(),A3()表示参数默认值
    return pair<T, int>{ T {a1, a2, a3}, 0 }; // T {xx,xx,xx}是个构造
}
// A1苹果,A2香蕉,A3大西瓜, T水果篮,pair.srcond 水果篮的个数

/* 有根本上的缺陷 
1.T 必须能准确接受3个参数进行构造 ,水果篮必须能接收这三个水果构造
2.出厂时最多只能调用3个参数 水果篮最多装三个水果
3.A1,A2, A3必须要有默认的构造函数 
4.a1, a2, a3必须pass by value ,如果想要用&和const&重载,会出现组合爆炸
*/
  • 方案1- variadic template 可变参数模板
template <class T, class... Args> //模板参数包
auto factory(Args... agrs){ //函数形数包
    return pair<T, int>{ T{args...}, 0}; //函数实参包
}
/*
1.解决了任意参数的问题 good!
2.没有解决pass by value的问题 bad!
*/
  • 方案2 Forwarding Reference 转发引用

Concepts & Constrained Templates


ysysys
10 声望1 粉丝