const修饰符的移除
让你来写移除const修饰符,你会怎么样来写?
😂😂trait
类模板,如下
#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;
}
退化技术
- 某些类型一旦传递给函数模板(通过函数模板来推断相关的类型),那么推断出来的类型就会产生退化。所谓退化(decay),就是把类型中的一些修饰符丢弃了。例如,const int中的const丢弃后,就变成int类型,那么对于const int类型,int类型就是一种退化的表现。
c++
标准库中有一个类模板std::decay
,这个类模板的作用就是把一个类型退化掉(就是把类型中的一些修饰符丢掉)。
std::decay<const int&>::type nb = 28;// nb的类型为int类型std::cout << "nb的类型为:" << typeid(decltype(nb)).name() << std::endl;
如何实现一个类似std::decay
功能的trait
类模板呢?
// 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;
}
- 上述函数代表类型:void()
- 可以使用函数指针指向某种函数类型,如果指向void(),函数指针应该是void(*)()
- 如果不为函数名退化为函数指针写一个Decay的特化版本,那么,传入testFunc2这个函数类型,
得到的返回类型依旧是void(),换句话说传入什么类型,就返回什么类型
#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;// 2Decay<decltype(testFunc2)>::type rfunc;std::cout << "rfunc类型为:" << typeid(decltype(rfunc)).name() << std::endl;rfunc();return 0;
}
现在容易理解写一个Decay特化版本把函数名(退化成)函数指针这件事了
因为函数可能有任何的返回类型以及任何数量和类型的参数,所以这个Decay的特化版本比较特殊
需要可变参模板来实现
#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// 2Decay<decltype(testFunc2)>::type rfunc;std::cout << "rfunc类型为:" << typeid(decltype(rfunc)).name() << std::endl;rfunc();
#endif // 3Decay<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
,代码如下
template<typename T>
using Decay_t = typename Decay<T>::type;
于是,main()
函数中的代码,可以写成
Decay<decltype(testFunc2)>::type rfunc;
就可以写成
Decay_t<decltype(testFunc2)> rfunc;