背景
定义在<type_traits>中,用于去除或者增加一个类型的 const 和 volatile 属性,属于比较基础的类型萃取技术。
代码实现
gcc 官网:https://gcc.gnu.org/
gcc 代码下载:https://mirrors.tuna.tsinghua.edu.cn/help/gcc.git/
gcc 版本代码:gcc-7.5.0(branch 分支)
文件位置:gcc/libstdc++-v3/include/std/type_traits
注意:以下的代码实现省略了一些宏定义部分,实际的代码还是参考 gcc 源码,这里仅用作相关原理分析。
实现分析
std::add_const/std::remove_const
/// remove_const
template<typename _Tp>
struct remove_const
{ typedef _Tp type; };
template<typename _Tp>
struct remove_const<_Tp const>
{ typedef _Tp type; };
/// add_const
template<typename _Tp>
struct add_const
{ typedef _Tp const type; };
复制代码
可以看到 add_const 是比较简单的,将输入类型加 const 修饰重定义为 type 即可;
remove_const 需要多一个模板处理,如果是普通情况(没有 const 修饰),直接重定义输入类型即可,
多出来的模板是基础模板的一个特化版本,cover 带有 const 属性的类型,去除 const 属性,重定义原类型为 type 即可。
注意,上面提到的模板特化将会在类型萃取中经常用到,用来覆盖某些特定类型的处理,这也是模板的一大优势。
std::add_volatile/std::remove_volatile
/// remove_volatile
template<typename _Tp>
struct remove_volatile
{ typedef _Tp type; };
template<typename _Tp>
struct remove_volatile<_Tp volatile>
{ typedef _Tp type; };
/// add_volatile
template<typename _Tp>
struct add_volatile
{ typedef _Tp volatile type; };
复制代码
实现方式与 const 的操作基本一致,就不深入讲解了
std::remove_cv/std::add_cv
/// remove_cv
template<typename _Tp>
struct remove_cv
{
typedef typename
remove_const<typename remove_volatile<_Tp>::type>::type type;
};
/// add_cv
template<typename _Tp>
struct add_cv
{
typedef typename
add_const<typename add_volatile<_Tp>::type>::type type;
};
复制代码
这里就是对上面的重复操作,需要注意的是,这里使用 typename 先获取 remove_volatile 之后的 type 类型,然后再调用 remove_const,同样也是使用 typename 获取 type 类型,然后将其重定义为 type 类型;add_cv 是相反的操作。
使用案例
#include <iostream>
#include <type_traits>
int main(){
typedef typename std::remove_cv<const int>::type type1;
typedef typename std::remove_cv<volatile int>::type type2;
typedef typename std::remove_cv<const volatile int>::type type3;
typedef typename std::remove_cv<const volatile int*>::type type4;
typedef typename std::remove_cv<int* const volatile>::type type5;
std::cout << std::boolalpha;
std::cout << "type1:const int\t\t\t" << std::is_same<type1,int>::value << "\t"
<< std::is_same<type1,const int>::value << std::endl;
std::cout << "type2:volatile int\t\t" << std::is_same<type2,int>::value << "\t"
<< std::is_same<type2,volatile int>::value << std::endl;
std::cout << "type3:const volatile int\t" << std::is_same<type3,int>::value << "\t"
<< std::is_same<type3,const volatile int>::value << std::endl;
std::cout << "type4:const volatile int*\t" << std::is_same<type4,int*>::value << "\t"
<< std::is_same<type4,const volatile int*>::value << std::endl;
std::cout << "type5:int* const volatile\t" << std::is_same<type5,int*>::value << "\t"
<< std::is_same<type5,int* const volatile>::value << std::endl;
return 0;
}
复制代码
注意上面前三种类型进行比较时,结果都与我们预期的一样,在对 const volatile int*进行 remove_cv 操作时,我们要明白,前面的 const volatile 是针对指针指向的内容的修饰,而非对指针的修饰,所以 remove_cv 并不会去除 const volatile,类型还是与 const volatile int*一样。
反过来将修饰符 const volatile 修饰指针,此时就能去除 const volatile 变成 int*指针。测试结果如下:
总结
std::remove_cv/std::remove_cv 是类型萃取的基础部分,使用了模板特化的方式,可以将输入类型中的 const volatile 修饰去除或者增加,最后都以成员 type 的形式展现,在具体使用过程中可以方便地对输入类型进行增加修饰和去除修饰的操作。
评论