首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

C++之std::decay

  • 24-03-18 06:43
  • 4051
  • 8424
blog.csdn.net

相关系列文章

C++之std::is_object

C++之std::decay

C++模板函数重载规则细说

C++之std::declval

C++之std::move(移动语义)

C++之std::forward(完美转发)

C++之std::enable_if

C++之std::is_pod(平凡的数据)

从C++容器中获取存储数据的类型

目录

1.简介

2.辅助类

2.1.std::remove_reference和std::remove_reference_t

2.2.std::remove_cv

2.3.std::is_array

2.4.std::is_function

2.5.std::remove_extent

2.6.std::add_pointer

2.7.std::conditional

3.可能实现

4.源码分析

5.使用

6.注意事项

7.总结


1.简介

std::decay是C++11之后引进的模板编程工具,它的主要作用是将给定的类型T转换为它的“衰变”类型。这个“衰变”类型是指去除类型T的所有引用、常量和易变性限定符,以及将所有数组和函数转换为对应的指针类型后得到的类型;在头文件  中定义:

  1. template< class T >
  2. struct decay;

2.辅助类

2.1.std::remove_reference和std::remove_reference_t

先看实现:

  1. template <class _Ty>
  2. struct remove_reference {
  3. using type = _Ty;
  4. using _Const_thru_ref_type = const _Ty;
  5. };
  6. template <class _Ty>
  7. struct remove_reference<_Ty&> { //左值引用
  8. using type = _Ty;
  9. using _Const_thru_ref_type = const _Ty&;
  10. };
  11. template <class _Ty>
  12. struct remove_reference<_Ty&&> { //右值引用
  13. using type = _Ty;
  14. using _Const_thru_ref_type = const _Ty&&;
  15. };
  16. template <class _Ty>
  17. using remove_reference_t = typename remove_reference<_Ty>::type;

从上面的代码实现来看,它的作用就是擦除类型引用,无论是左值引用还是右值引用,例如,如果_Ty是int&或int&&,那么std::remove_reference_t<_Ty>就是int。示例如下:

  1. #include // std::cout
  2. #include // std::is_same
  3. template<class T1, class T2>
  4. void print_is_same() {
  5. std::cout << std::is_same() << '\n';
  6. }
  7. int main() {
  8. std::cout << std::boolalpha;
  9. print_is_same<int, int>(); //输出: true
  10. print_is_same<int, int &>(); //输出: false
  11. print_is_same<int, int &&>(); //输出: false
  12. print_is_same<int, std::remove_reference<int>::type>(); //输出: true
  13. print_is_same<int, std::remove_reference<int &>::type>(); //输出: true
  14. print_is_same<int, std::remove_reference<int &&>::type>(); //输出: true
  15. }

2.2.std::remove_cv

用于擦除类型的const和volatile限定符,返回的是一个去除了const和volatile限定符的类型,例如,如果T是const int或volatile int,那么std::remove_cv_t就是int。它的实现如下:

  1. template <class _Ty>
  2. struct remove_cv { // remove top-level const and volatile qualifiers
  3. using type = _Ty;
  4. template <template <class> class _Fn>
  5. using _Apply = _Fn<_Ty>; // apply cv-qualifiers from the class template argument to _Fn<_Ty>
  6. };
  7. template <class _Ty>
  8. struct remove_cv<const _Ty> { //擦除const
  9. using type = _Ty;
  10. template <template <class> class _Fn>
  11. using _Apply = const _Fn<_Ty>;
  12. };
  13. template <class _Ty>
  14. struct remove_cv<volatile _Ty> { //擦除volatile
  15. using type = _Ty;
  16. template <template <class> class _Fn>
  17. using _Apply = volatile _Fn<_Ty>;
  18. };
  19. template <class _Ty>
  20. struct remove_cv<const volatile _Ty> { //擦除const volatile
  21. using type = _Ty;
  22. template <template <class> class _Fn>
  23. using _Apply = const volatile _Fn<_Ty>;
  24. };
  25. template <class _Ty>
  26. using remove_cv_t = typename remove_cv<_Ty>::type;

示例如下:

  1. #include
  2. #include
  3. int main() {
  4. typedef std::remove_cv<const int>::type type1;
  5. typedef std::remove_cv<volatile int>::type type2;
  6. typedef std::remove_cv<const volatile int>::type type3;
  7. typedef std::remove_cv<const volatile int*>::type type4;
  8. typedef std::remove_cv<int * const volatile>::type type5;
  9. std::cout << "test1 " << (std::is_same<int, type1>::value
  10. ? "passed" : "failed") << '\n';
  11. std::cout << "test2 " << (std::is_same<int, type2>::value
  12. ? "passed" : "failed") << '\n';
  13. std::cout << "test3 " << (std::is_same<int, type3>::value
  14. ? "passed" : "failed") << '\n';
  15. std::cout << "test4 " << (std::is_same<const volatile int*, type4>::value
  16. ? "passed" : "failed") << '\n';
  17. std::cout << "test5 " << (std::is_same<int*, type5>::value
  18. ? "passed" : "failed") << '\n';
  19. }

输出:

  1. test1 passed
  2. test2 passed
  3. test3 passed
  4. test4 passed
  5. test5 passed

2.3.std::is_array

  1. template< class T >
  2. struct is_array;

主要是判断 T 是否为数组类型,如果 T 为数组类型,那么成员常量 value 等于 true。对于其它类型,value 等于 false,  它的实现如下:

  1. template <class>
  2. _INLINE_VAR constexpr bool is_array_v = false; // determine whether type argument is an array
  3. template <class _Ty, size_t _Nx>
  4. _INLINE_VAR constexpr bool is_array_v<_Ty[_Nx]> = true;
  5. template <class _Ty>
  6. _INLINE_VAR constexpr bool is_array_v<_Ty[]> = true;
  7. template <class _Ty>
  8. struct is_array : bool_constant> {};

从源码很清晰的看到当输入为数组类型的时候,特例化返回true。示例如下:

  1. #include
  2. #include
  3. #include
  4. class A {};
  5. int main()
  6. {
  7. std::cout << std::boolalpha;
  8. std::cout << std::is_array::value << '\n'; //输出: false
  9. std::cout << std::is_array::value << '\n'; //输出: true
  10. std::cout << std::is_array3]>::value << '\n'; //输出: true
  11. std::cout << std::is_array<float>::value << '\n'; //输出: false
  12. std::cout << std::is_array<int>::value << '\n'; //输出: false
  13. std::cout << std::is_array<int[]>::value << '\n'; //输出: true
  14. std::cout << std::is_array<int[3]>::value << '\n'; //输出: true
  15. std::cout << std::is_arrayint, 3>>::value << '\n'; //输出: false
  16. }

2.4.std::is_function

std::is_function(C++11)用于检查类型是否为函数,注意此处函数类型不包括std::function, lambda, 有重载operator()的类,它的实现如下:

  1. template <class _Ty>
  2. _INLINE_VAR constexpr bool is_function_v = // only function types and reference types can't be const qualified
  3. !is_const_v<const _Ty> && !is_reference_v<_Ty>;
  4. template <class _Ty>
  5. struct is_function : bool_constant> {};

示例如下:

  1. #include
  2. #include
  3. struct A {
  4. int fun() const&;
  5. };
  6. template<typename>
  7. struct PM_traits {};
  8. template<class T, class U>
  9. struct PM_traits {
  10. using member_type = U;
  11. };
  12. int f();
  13. int main()
  14. {
  15. std::cout << std::boolalpha;
  16. std::cout << std::is_function::value << '\n'; //输出 : false
  17. std::cout << std::is_function<int(int)>::value << '\n'; //输出 : true
  18. std::cout << std::is_function<decltype(f)>::value << '\n'; //输出 : true
  19. std::cout << std::is_function<int>::value << '\n'; //输出 : false
  20. using T = PM_traits<decltype(&A::fun)>::member_type; // T 为 int() const&
  21. std::cout << std::is_function::value << '\n'; //输出 : true
  22. }

2.5.std::remove_extent

它的作用是给数组移除一个维度,注意若 T 是多维数组,则只移除第一维。它的实现如下:

  1. template <class _Ty>
  2. struct remove_extent { // remove array extent
  3. using type = _Ty;
  4. };
  5. template <class _Ty, size_t _Ix>
  6. struct remove_extent<_Ty[_Ix]> {
  7. using type = _Ty;
  8. };
  9. template <class _Ty>
  10. struct remove_extent<_Ty[]> {
  11. using type = _Ty;
  12. };
  13. template <class _Ty>
  14. using remove_extent_t = typename remove_extent<_Ty>::type;

多维数组,如果要全部移除维度,则要用std::remove_all_extents, std::remove_all_extents实现如下:

  1. template <class _Ty>
  2. struct remove_all_extents { // remove all array extents
  3. using type = _Ty;
  4. };
  5. template <class _Ty, size_t _Ix>
  6. struct remove_all_extents<_Ty[_Ix]> {
  7. using type = typename remove_all_extents<_Ty>::type;
  8. };
  9. template <class _Ty>
  10. struct remove_all_extents<_Ty[]> {
  11. using type = typename remove_all_extents<_Ty>::type;
  12. };
  13. template <class _Ty>
  14. using remove_all_extents_t = typename remove_all_extents<_Ty>::type;

这里巧妙的借助元编程中模板类的偏特化和递归继承属性: 定义的remove_all_extents偏特化版本继承自身,通过不断地递归继承,直到remove_all_extents类型与T类型完全相同时调用普通版本,完成递归调用;所有的递归调用过程均在编译期完成,从这个例子中可以看出模板元编程的真正魅力所在。

示例如下:

  1. #include
  2. #include
  3. #include
  4. #include
  5. template<class A>
  6. typename std::enable_if< std::rank::value == 1 >::type
  7. print_1d(const A& a)
  8. {
  9. copy(a, a+std::extent::value,
  10. std::ostream_iterator<typename std::remove_extent::type>(std::cout, " "));
  11. std::cout << '\n';
  12. }
  13. int main()
  14. {
  15. int a[][3] = {{1,2,3},{4,5,6}};
  16. // print_1d(a); // 编译时错误
  17. print_1d(a[1]); //输出: 4 5 6
  18. }

2.6.std::add_pointer

std::add_pointer模板可以把一个类型转换为指针类型,它的实现:

  1. template <class _Ty, class = void>
  2. struct _Add_pointer { // add pointer (pointer type cannot be formed)
  3. using type = _Ty;
  4. };
  5. template <class _Ty>
  6. struct _Add_pointer<_Ty, void_t<remove_reference_t<_Ty>*>> { // (pointer type can be formed)
  7. using type = remove_reference_t<_Ty>*;
  8. };
  9. template <class _Ty>
  10. struct add_pointer {
  11. using type = typename _Add_pointer<_Ty>::type;
  12. };
  13. template <class _Ty>
  14. using add_pointer_t = typename _Add_pointer<_Ty>::type;

示例如下:

  1. #include
  2. #include
  3. using namespace std;
  4. int main() {
  5. typedef add_pointer<int>::type ptr; // ptr 是 int* 类型
  6. cout << is_same<int*, ptr>::value << endl; // 输出 true
  7. return 0;
  8. }

        在上面例子中,我们使用add_pointer模板将类型int转换为指针类型int*,并声明了别名ptr。可以看到,使用add_pointer模板可以快速实现类型转换。

2.7.std::conditional

std::conditional模板可以根据条件进行类型选择, 实现如下:

  1. template <bool _Test, class _Ty1, class _Ty2>
  2. struct conditional { // Choose _Ty1 if _Test is true, and _Ty2 otherwise
  3. using type = _Ty1;
  4. };
  5. template <class _Ty1, class _Ty2>
  6. struct conditional<false, _Ty1, _Ty2> {
  7. using type = _Ty2;
  8. };
  9. template <bool _Test, class _Ty1, class _Ty2>
  10. using conditional_t = typename conditional<_Test, _Ty1, _Ty2>::type;

示例如下:

  1. #include
  2. #include
  3. #include
  4. int main()
  5. {
  6. typedef std::conditional<true, int, double>::type a;
  7. typedef std::conditional<false, int, double>::type b;
  8. typedef std::conditional<sizeof(int) >= sizeof(double), int, double>::type c;
  9. std::cout << typeid(a).name() << '\n';
  10. std::cout << typeid(b).name() << '\n';
  11. std::cout << typeid(c).name() << '\n';
  12. }

3.可能实现

介绍完上面的基础知识后,根据std::decay的功能,猜测大致可能的实现如下:

  1. template<class T>
  2. struct decay
  3. {
  4. private:
  5. typedef typename std::remove_reference::type U;
  6. public:
  7. typedef typename std::conditional<
  8. std::is_array::value,
  9. typename std::remove_extent::type*,
  10. typename std::conditional<
  11. std::is_function::value,
  12. typename std::add_pointer::type,
  13. typename std::remove_cv::type
  14. >::type
  15. >::type type;
  16. };

分析步骤:

1)  擦除引用类型。例如 int&和int&& 返回 int

2)如果是数组,返回的就是数组存放的数据类型。例如:int[10] 返回 int

3)如果是函数,返回的就是函数指针。例如:int (int) 返回 int (*)(int)

4)  除2和3之外的其它类型,则擦除const、volatile 或 const volatile。

4.源码分析

根据第3部分讲解的实现流程,在VS2019上是怎么实现上面的功能呢?不隐藏,直接上源码:

  1. template <bool>
  2. struct _Select { // Select between aliases that extract either their first or second parameter
  3. template <class _Ty1, class>
  4. using _Apply = _Ty1;
  5. };
  6. template <>
  7. struct _Select<false> {
  8. template <class, class _Ty2>
  9. using _Apply = _Ty2;
  10. };
  11. template <class _Ty>
  12. struct decay { // determines decayed version of _Ty
  13. using _Ty1 = remove_reference_t<_Ty>;
  14. using _Ty2 = typename _Select>::template _Apply, remove_cv<_Ty1>>;
  15. using type = typename _Select>::template _Applyremove_extent_t<_Ty1>>, _Ty2>::type;
  16. };
  17. template <class _Ty>
  18. using decay_t = typename decay<_Ty>::type;

从上面的代码可以看出,把std::conditional替换成了_Select,而_Select的功能和std::conditional是一模一样的,代码的逻辑和第3部分分析的也是一模一样的,在这里就不再过多赘述了。

5.使用

1)在之前我的从C++容器中获取存储数据的类型-CSDN博客中,从容器中获取所存储的数据类型,就用到了std::decay,在函数func获取跟容器中一样数据类型,如下代码:

  1. #include
  2. #include
  3. template <typename Container>
  4. void func(Container& t, const char* pMessage)
  5. {
  6. using TYPE = std::decay<decltype(*t.begin())>;
  7. std::cout << pMessage << boost::typeindex::type_id_with_cvr<decltype(TYPE)>().pretty_name() << std::endl;
  8. }

调用函数decltype(*t.begin()出来还是引用类型,通过std::decay退变成真正的原始类型Container::value_type。

2)示例如下:

  1. #include
  2. #include
  3. template <typename T, typename U>
  4. struct decay_equiv :
  5. std::is_same<typename std::decay::type, U>::type
  6. {};
  7. int main()
  8. {
  9. std::cout << std::boolalpha
  10. << decay_equiv<int, int>::value << '\n' /*1*/
  11. << decay_equiv<int&, int>::value << '\n' /*2*/
  12. << decay_equiv<int&&, int>::value << '\n' /*3*/
  13. << decay_equiv<const int&, int>::value << '\n' /*4*/
  14. << decay_equiv<int[2], int*>::value << '\n' /*5*/
  15. << decay_equiv<int(int), int(*)(int)>::value << '\n'; /*6*/
  16. }

上面的代码第1部分是传入int,就是普通类型,传出也是int

上面的代码第2部分是传入int&,通过std::decay擦除左值引用,传出int

上面的代码第3部分是传入int&&,通过std::decay擦除右值引用,传出int

上面的代码第4部分是传入const int&,通过std::decay擦除左值引用,去掉const,传出int

上面的代码第5部分是传入一维数组int[2],通过std::decay移除一个维度,退化传出int*

上面的代码第6部分是传入函数int(int),通过std::decay添加指针,传出指向传入函数的指针int(*)(int)。

6.注意事项

1、虽然std::decay可以处理许多类型的转换,但是它不能处理所有的类型。例如,std::decay不能处理类类型,枚举类型,和联合类型。

2、std::decay只能用于模板参数。如果我们尝试在非模板参数上使用std::decay,编译器将会报错。

3、std::decay不能用于消除指针类型。如果我们尝试在指针类型上使用std::decay,std::decay将不会有任何效果。

7.总结

std::decay是我们平时模版编程中使用的比较多的,在实际模板编程或者模板元编程中非常有用,在type_traits源代码里处处可见,实际工作中也会经常用到;上面的介绍只是抛砖引玉,要想真正掌握它,只有的不停地使用,在使用过程中遇到问题,解决问题,才能真正理解它的原理,灵活使用。

注:本文转载自blog.csdn.net的流星雨爱编程的文章"https://blog.csdn.net/haokan123456789/article/details/135278508"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top