c++新经典模板与泛型编程:const修饰符的移除与增加

const修饰符的移除

让你来写移除const修饰符,你会怎么样来写?

😂😂trait类模板,如下

cpp 复制代码
#include <iostream>

// 泛化版本
template<typename T>
struct RemoveConst
{
	using type = T;
};

// 特化版本
template<typename T>
struct RemoveConst<const T>
{
	using type = T;
};

// 根据需要,可能还要增加其他特化版本
template<typename T>
using RemoveConst_t = typename RemoveConst<T>::type;


int main()
{
	// nca 是int类型
	// c++标准库中的std::remove_const也比较类似
	RemoveConst_t<const int> nca = 15;

	// 可以给nca重新赋值
	nca = 18;
	
	return 0;
}

退化技术

  1. 某些类型一旦传递给函数模板(通过函数模板来推断相关的类型),那么推断出来的类型就会产生退化。所谓退化(decay),就是把类型中的一些修饰符丢弃了。例如,const int中的const丢弃后,就变成int类型,那么对于const int类型,int类型就是一种退化的表现。
  2. c++标准库中有一个类模板std::decay,这个类模板的作用就是把一个类型退化掉(就是把类型中的一些修饰符丢掉)。
cpp 复制代码
	std::decay<const int&>::type nb = 28;
	// nb的类型为int类型
	std::cout << "nb的类型为:" << typeid(decltype(nb)).name() << std::endl;

如何实现一个类似std::decay功能的trait类模板呢?

cpp 复制代码
// b.cpp
int g_array[10];

// main.cpp
#include <iostream>

// 泛化版本
template<typename T>
struct RemoveReference
{
	using type = T;
};

// 特化版本
template<typename T>
struct RemoveReference<T&>
{
	using type = T;
};
template<typename T>
struct RemoveReference<T&&>  // 这个特化能适应 const T&&应该是伴随我一生,难以理解的噩梦了
{
	using type = T;
};

// 泛化版本
template<typename T>
struct RemoveConst
{
	using type = T;
};

// 特化版本
template<typename T>
struct RemoveConst<const T>
{
	using type = T;
};

// 根据需要,可能还要增加其他特化版本
template<typename T>
using RemoveConst_t = typename RemoveConst<T>::type;

template<typename T>
struct RemoveCR : RemoveConst<typename RemoveReference<T>::type>
{ // 把const和引用修饰符去掉
};

template<typename T>
using RemoveCR_t = typename RemoveCR<T>::type;

// Decay的trait类模板
// 泛化版本
template<typename T>
struct Decay : RemoveCR<T>
{
};


// 特化版本,这个特化版本没有继承任何父类
// 有边界数组转换成指针
template<typename T,std::size_t size>
struct Decay<T[size]>
{
	using type = T*;
};

// 无边界数组转换成指针
template<typename T>
struct Decay<T[]>
{
	using type = T*;
};

extern int g_array[];


int main()
{
	RemoveCR_t<const int&&> rcrobj = 15; // rcrobj为int类型,只能叹为观止,惊叹rcrobj鬼斧神工地成了int类型

	int arr[2] = { 1,2 };
	Decay<decltype(arr)>::type my_array;
	std::cout << "my_array的类型为: " << typeid(decltype(my_array)).name() << std::endl;

	Decay<decltype(g_array)>::type my_array_2;
	std::cout << "my_array_2的类型为:" << typeid(decltype(my_array_2)).name() << std::endl;


	return 0;
}
  1. 上述函数代表类型:void()
  2. 可以使用函数指针指向某种函数类型,如果指向void(),函数指针应该是void(*)()
  3. 如果不为函数名退化为函数指针写一个Decay的特化版本,那么,传入testFunc2这个函数类型,
    得到的返回类型依旧是void(),换句话说传入什么类型,就返回什么类型
cpp 复制代码
#include <iostream>

// 泛化版本
template<typename T>
struct RemoveReference
{
	using type = T;
};

// 特化版本
template<typename T>
struct RemoveReference<T&>
{
	using type = T;
};
template<typename T>
struct RemoveReference<T&&>  // 这个特化能适应 const T&&应该是伴随我一生,难以理解的噩梦了
{
	using type = T;
};

// 泛化版本
template<typename T>
struct RemoveConst
{
	using type = T;
};

// 特化版本
template<typename T>
struct RemoveConst<const T>
{
	using type = T;
};

// 根据需要,可能还要增加其他特化版本
template<typename T>
using RemoveConst_t = typename RemoveConst<T>::type;

template<typename T>
struct RemoveCR : RemoveConst<typename RemoveReference<T>::type>
{ // 把const和引用修饰符去掉
};

template<typename T>
using RemoveCR_t = typename RemoveCR<T>::type;

// Decay的trait类模板
// 泛化版本
template<typename T>
struct Decay : RemoveCR<T>
{
};


// 特化版本,这个特化版本没有继承任何父类
// 有边界数组转换成指针
template<typename T,std::size_t size>
struct Decay<T[size]>
{
	using type = T*;
};

// 无边界数组转换成指针
template<typename T>
struct Decay<T[]>
{
	using type = T*;
};

extern int g_array[];

// 简单的函数
void testFunc2()
{
	std::cout << "testFunc2()执行了" << std::endl;
}

void rfunc()
{
	std::cout << "rfunc执行了" << std::endl;
}



int main()
{
	RemoveCR_t<const int&&> rcrobj = 15; // rcrobj为int类型,只能叹为观止,惊叹rcrobj鬼斧神工地成了int类型

	int arr[2] = { 1,2 };
	Decay<decltype(arr)>::type my_array;
	std::cout << "my_array的类型为: " << typeid(decltype(my_array)).name() << std::endl;

	Decay<decltype(g_array)>::type my_array_2;
	std::cout << "my_array_2的类型为:" << typeid(decltype(my_array_2)).name() << std::endl;

	// 2
	Decay<decltype(testFunc2)>::type rfunc;
	std::cout << "rfunc类型为:" << typeid(decltype(rfunc)).name() << std::endl;
	rfunc();


	return 0;
}

现在容易理解写一个Decay特化版本把函数名(退化成)函数指针这件事了

因为函数可能有任何的返回类型以及任何数量和类型的参数,所以这个Decay的特化版本比较特殊

需要可变参模板来实现

cpp 复制代码
#include <iostream>

// 泛化版本
template<typename T>
struct RemoveReference
{
	using type = T;
};

// 特化版本
template<typename T>
struct RemoveReference<T&>
{
	using type = T;
};
template<typename T>
struct RemoveReference<T&&>  // 这个特化能适应 const T&&应该是伴随我一生,难以理解的噩梦了
{
	using type = T;
};

// 泛化版本
template<typename T>
struct RemoveConst
{
	using type = T;
};

// 特化版本
template<typename T>
struct RemoveConst<const T>
{
	using type = T;
};

// 根据需要,可能还要增加其他特化版本
template<typename T>
using RemoveConst_t = typename RemoveConst<T>::type;

template<typename T>
struct RemoveCR : RemoveConst<typename RemoveReference<T>::type>
{ // 把const和引用修饰符去掉
};

template<typename T>
using RemoveCR_t = typename RemoveCR<T>::type;

// Decay的trait类模板
// 泛化版本
template<typename T>
struct Decay : RemoveCR<T>
{
};


// 特化版本,这个特化版本没有继承任何父类
// 有边界数组转换成指针
template<typename T,std::size_t size>
struct Decay<T[size]>
{
	using type = T*;
};

// 无边界数组转换成指针
template<typename T>
struct Decay<T[]>
{
	using type = T*;
};

extern int g_array[];

// 简单的函数
void testFunc2()
{
	std::cout << "testFunc2()执行了" << std::endl;
}

// 3
template<typename T,typename... Args>
struct Decay<T(Args...)> // 返回类型是T,参数是Args...
{
	using type = T(*)(Args...);
};

int main()
{
	RemoveCR_t<const int&&> rcrobj = 15; // rcrobj为int类型,只能叹为观止,惊叹rcrobj鬼斧神工地成了int类型

	int arr[2] = { 1,2 };
	Decay<decltype(arr)>::type my_array;
	std::cout << "my_array的类型为: " << typeid(decltype(my_array)).name() << std::endl;

	Decay<decltype(g_array)>::type my_array_2;
	std::cout << "my_array_2的类型为:" << typeid(decltype(my_array_2)).name() << std::endl;

#if 0
	// 2
	Decay<decltype(testFunc2)>::type rfunc;
	std::cout << "rfunc类型为:" << typeid(decltype(rfunc)).name() << std::endl;
	rfunc();
#endif 

	// 3
	Decay<decltype(testFunc2)>::type rfunc_1;
	std::cout << "rfunc类型为:" << typeid(decltype(rfunc_1)).name() << std::endl;
	rfunc_1 = testFunc2;
	rfunc_1();

	return 0;
}


别名模板的威力

通过别名模板把Decay::type类型名简化成Decay_t,代码如下

cpp 复制代码
template<typename T>
using Decay_t = typename Decay<T>::type;

于是,main()函数中的代码,可以写成

cpp 复制代码
Decay<decltype(testFunc2)>::type rfunc;

就可以写成

cpp 复制代码
Decay_t<decltype(testFunc2)> rfunc;
相关推荐
打鱼又晒网5 个月前
适配器底层源码解析及实现——STL源码剖析第八章的总结回顾
c++·stl·适配器模式·萃取·底层源码
ThorKing011 年前
萃取和constexpr
c++·1024程序员节·萃取