C++11的std::is_same和std::decay使用与源码解析

Posted 彼 方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11的std::is_same和std::decay使用与源码解析相关的知识,希望对你有一定的参考价值。

1、源码准备

本文是基于gcc-4.9.0的源代码进行分析,std::is_same和std::decay是C++11才加入标准的,所以低版本的gcc源码是没有这个的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的,下面给出源码下载地址
http://ftp.gnu.org/gnu/gcc

2、使用方法

std::is_samestd::decay是C++11新增的两个模板类,功能分别是类型判断和类型朽化。

2.1、std::is_same使用方法

首先通过一个例子看一下std::is_same的使用方法:

#include <iostream>
#include <type_traits>
#include <stdint.h>

int main(int argc, char* argv[])
{
    std::cout << "第一组,类型的写法完全相同" << std::endl;
    std::cout << std::is_same<int, int>::value << std::endl;         // true
    std::cout << std::is_same<double, double>::value << std::endl;   // true
    std::cout << std::is_same<int32_t, int32_t>::value << std::endl; // true
    std::cout << std::is_same<int64_t, int64_t>::value << std::endl; // true

    std::cout << std::endl << "第二组,类型相同,但是写法不同(typedef重命名了)" << std::endl;
    std::cout << std::is_same<short, int16_t>::value << std::endl;   // true
    std::cout << std::is_same<int, int32_t>::value << std::endl;     // true
    std::cout << std::is_same<long, int64_t>::value << std::endl;    // true
    std::cout << std::is_same<unsigned int, uint32_t>::value << std::endl; // true

    std::cout << std::endl << "第三组,类型不同" << std::endl;
    std::cout << std::is_same<char, int>::value << std::endl;        // false
    std::cout << std::is_same<double, float>::value << std::endl;    // false
    std::cout << std::is_same<float, long>::value << std::endl;      // false
    std::cout << std::is_same<double, char>::value << std::endl;     // false

    std::cout << std::endl << "第四组,类型其实是一样的,只是省略写法而已" << std::endl;
    std::cout << std::is_same<char, signed char>::value << std::endl;   // false
    std::cout << std::is_same<char, unsigned char>::value << std::endl; // false
    std::cout << std::is_same<short, signed short>::value << std::endl; // true
    std::cout << std::is_same<int, signed int>::value << std::endl;     // true

    std::cout << std::endl << "第五组,类型不同" << std::endl;
    std::cout << std::is_same<char*, char[]>::value << std::endl;           // false
    std::cout << std::is_same<const char, char>::value << std::endl;        // false
    std::cout << std::is_same<const char&, const char>::value << std::endl; // false
    std::cout << std::is_same<const int&, int>::value << std::endl;         // false
}

上面的结果中,前三组应该是没啥歧义的,因为std::is_same是可以识别出被typedefdefine重命名的类型的原始类型的。第四组就有点意思了,除了前面两个以外都是true,说明std::is_same是可以判断出signedsigned+类型是相同的。第五组的话也没什么好说的,类型确实不同。

这里解释一下,从上面的代码中看得出来,char既不是unsigned char也不是signed charchar的表达数值范围可能等同于signed char,也可能等同于unsigned char,这取决于编译器,一般是等同于signed char,但这仅仅是范围等同,就像32位机上intlong的数值范围是一样的,但并不是同一个类型。

2.2、std::decay使用方法

std::decay人如其名,就是朽化的意思,主要是对一个类型进行朽化处理,说白一点就是将类型的一些属性比如constvolatile&(引用)等消除掉,需要注意指针是消不掉。首先通过一个例子看一下std::decay的使用方法:

#include <iostream>
#include <type_traits>
#include <stdint.h>

int main(int argc, char* argv[])
{
    const int a = 10l;
    // 报错,const修饰的不能改变
    //a = 10;

    std::decay<const int>::type b = 101;
    // 可以修改
    b = 10;
}

从上面的例子可以看出经过std::decay处理之后,const intconst属性就没了,这就是std::decay的一个典型用法

2.3、std::is_same和std::decay配合使用

代码如下,可以看到,经过std::decay朽化之后,原本使用std::is_same判断不相同的类型也变得相同了,这里需要注意的是直接使用std::is_same去判断char*char[],显示是不相同的,但是经过朽化之后这两个就变得相同了,这个情况原理在后面会讲。

#include <iostream>
#include <type_traits>
#include <stdint.h>

int main(int argc, char* argv[])
{
    std::cout << std::is_same<std::decay<int>::type, std::decay<int>::type>::value << std::endl;       // true
    std::cout << std::is_same<std::decay<const int>::type, std::decay<int>::type>::value << std::endl; // true
    std::cout << std::is_same<std::decay<int&>::type, std::decay<int>::type>::value << std::endl;      // true
    std::cout << std::is_same<std::decay<const int>::type, std::decay<const volatile int>::type>::value << std::endl; // true
    std::cout << std::is_same<std::decay<const volatile int&>::type, std::decay<int>::type>::value << std::endl; // true
    std::cout << std::is_same<std::decay<char*>::type, std::decay<char[]>::type>::value << std::endl; // true
}

3、源码解析

3.1、std::is_same源码解析

std::is_same位于libstdc++-v3\\include\\std\\type_traits

template<typename, typename>
struct is_same : public false_type { };

template<typename _Tp>
struct is_same<_Tp, _Tp> : public true_type { };

可以看到,代码非常简单,第一个是泛化形式,第二个是偏特化形式,当两个类型相同时,调用的是第二个,而第二个继承的是true_type,所以此时is_same<_Tp, _Tp>::value的值就是true;相反的,当两个类型不同时,调用第一个,此时is_same<_Tp, _Tp>::value的值就是false

3.2、std::decay源码解析

std::decay位于libstdc++-v3\\include\\std\\type_traits

template<typename _Tp> 
class decay 
{ 
    typedef typename remove_reference<_Tp>::type __remove_type;

public:
    typedef typename __decay_selector<__remove_type>::__type type;
};

可以看到,首先使用了std::remove_reference_Tp的引用属性去除了,关于std::remove_reference的内容之前在这篇文章《C++11的右值引用、移动语义(std::move)和完美转发(std::forward)详解》中有讲过,这里就不赘述了,大家可以自己去看一下。然后使用了std::__decay_selector,下面看一下std::__decay_selector的源码

template<typename _Up, 
    bool _IsArray = is_array<_Up>::value,
    bool _IsFunction = is_function<_Up>::value> 
struct __decay_selector;

template<typename _Up>
struct __decay_selector<_Up, false, false>
{ typedef typename remove_cv<_Up>::type __type; };

template<typename _Up>
struct __decay_selector<_Up, true, false>
{ typedef typename remove_extent<_Up>::type* __type; };

template<typename _Up>
struct __decay_selector<_Up, false, true>
{ typedef typename add_pointer<_Up>::type __type; };

可以看到共有三种偏特化形式:

  1. 第一种就是当_Up既不是数组形式也不是可调用实体形式时匹配,使用了std::remove_cv去除了_Upconstvolatile属性。关于std::remove_cv可以看一下这篇文章《C++11的std::ref、std::cref源码解析》,里面有详细介绍
  2. 第二种就是当_Up是数组时匹配,使用了std::remove_cv去除了_Up的数组属性,只留下个数组名指针,这就是为什么前面使用std::decay之后char*char[]会显示相同的原因了。
template<typename _Tp>
struct remove_extent
{ typedef _Tp     type; };

template<typename _Tp, std::size_t _Size>
struct remove_extent<_Tp[_Size]>
{ typedef _Tp     type; };

template<typename _Tp>
struct remove_extent<_Tp[]>
{ typedef _Tp     type; };
  1. 第三种就是当_Up是可调用实体时匹配,使用了std::add_pointer去除了_Up的引用属性,代码与前面的那些原理是一样的,这里就不细讲了,大家可以自己去看一下源代码的实现。

综上可得std::decay其实就是利用模板特化机制来实现对类型进行筛选,将原始的、没有经过其它属性修饰的类型提取出来,以达到朽化类型的作用。

4、总结

本文先是介绍了C++11新增的std::is_samestd::decay这两个模板类的作用与使用方法,然后通过对源码进行分析,我们知道了这两个模板类的实现其实并不复杂,都是借助了模板特化机制来实现的,其实不止这两个,C++标准库的type_traits文件里面的内容基本都是使用类似的方法来实现的。

最后,如果大家觉得本文写得好的话麻烦点赞收藏关注一下谢谢,也可以关注该专栏,以后会有更多优质文章输出的。

以上是关于C++11的std::is_same和std::decay使用与源码解析的主要内容,如果未能解决你的问题,请参考以下文章

模板编程之std::is_same和std::decay

使用std :: is_same和operator ||进行编译错误

cppyy 和 std::is_same_v (C++17)

在 vc++ 中使用模板限制类型

VS2013 中的 SFINAE

右值重载? [复制]