type_traits 类型萃取与可变参数模板

一、类型萃取

type_traits 实现了在编译期计算、查询、判断、转换和选择类型的能力,使得我们在编译期就能做到优化改进和排错。

实现如下:

template <class T, T v>
struct integral_constant {
  static const T value = v;
  typedef T value_type;
  typedef integral_constant<T,v> type;
  constexpr operator value_type() { return v; }
};

typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;

true_type和false_type是integral_constant的两个实例, 定义了编译期的true和false类型。type_traits的类型判断就是通过继承这两个实例而实现的。

type_traits中的判断类型的接口有很多,在这里只看几个接口,其他的接口也是相同的道理。

1、判断是否是const修饰的类型

template<class T> struct is_const;

下面通过一个例子来看看他的效果。

#include <iostream>
#include <type_traits>
using namespace std;
int main()
{	
	cout << "is_const:" << endl;
	cout << "int:" << is_const<int>::value << endl;
	cout << "const int:" << is_const<const int>::value << endl;
	cout << "int*:" << is_const<int*>::value << endl;
	cout << "const int*:" << is_const<const int*>::value << endl;
	cout << "int* const:" << is_const<int* const>::value << endl;
	cout << "float:" << is_const<float>::value << endl;
	cout << "const float:" << is_const<const float>::value << endl;

	return 0;
}

输出如下:

is_const:
int:0
const int:1
int*:0
const int*:0
int* const:1
float:0
const float:1

大部分情况能正常判断出来,但是const int*为什么没有判断出来呢?

const int* 中的const是修饰的指定所指向的内容,表示指针的内容不能修改,但是由他定义的指针变量是可以修改的。

由此推测is_const只是判断由形参类型定义的变量。

下面来看来他是如何实现的:

#include <iostream>
#include <type_traits>
using namespace std;

template <class T>
struct my_is_const : std::false_type
{};

template <class T>
struct my_is_const<T const> : std::true_type
{};

int main()
{
    cout << "is_const:" << endl;
    cout << "int:" << my_is_const<int>::value << endl;
    cout << "const int:" << my_is_const<const int>::value << endl;
    cout << "int*:" << my_is_const<int*>::value << endl;
    cout << "const int*:" << my_is_const<const int*>::value << endl;
    cout << "int* const:" << my_is_const<int* const>::value << endl;
    cout << "float:" << my_is_const<float>::value << endl;
    cout << "const float:" << my_is_const<const float>::value << endl;
}

运行这段代码发现其输出是如标准的is_const输出一致,那么is_const的实现就是类型这样的代码。

那么这段代码是如何实现判断类型是否是const修饰的呢?

首先来看这段代码:

template <class T>
struct my_is_const : std::false_type
{};

这段代码很好理解,std::false_type表示的是一个false常量,就是说用这个模板类实例后其值都是false,也就是非const修饰的。

那么什么时候会实例化为true呢,关键在下面的代码:

template <class T>
struct my_is_const<T const> : std::true_type
{};

这段代码用到的模板的特化。

template <class T> struct my_is_const<T const>  在这行代码中,const 就是一个类型范围的特化。

当我们实例my_is_const<const int>时,由于在这里有个const,所以会匹配到特化的版本,这里我们继承的是true类型,所以编译器就会将他实现化true的类型,我们通过true类型的value就能判断出来是否是const修饰的了。

 

前面我们说过标准的is_const在判断const int*类型时,判断其不是const的,根据上面的原理我们可不可以自己实现判断其是const的呢。代码如下:

#include <iostream>
#include <type_traits>
using namespace std;

template <class T>
struct my_is_const : std::false_type
{};

template <class T>
struct my_is_const<T const> : std::true_type
{};

template <class T>
struct my_is_const<const T> : std::true_type
{

}

int main()
{
    cout << "is_const:" << endl;
    cout << "int:" << my_is_const<int>::value << endl;
    cout << "const int:" << my_is_const<const int>::value << endl;
    cout << "int*:" << my_is_const<int*>::value << endl;
    cout << "const int*:" << my_is_const<const int*>::value << endl;
    cout << "int* const:" << my_is_const<int* const>::value << endl;
    cout << "float:" << my_is_const<float>::value << endl;
    cout << "const float:" << my_is_const<const float>::value << endl;
}

编译时报错了 

my_is_const.cpp:14:8: error: redefinition of ‘struct my_is_const<const T>’
   14 | struct my_is_const<const T> : std::true_type
      |        ^~~~~~~~~~~~~~~~~~~~
my_is_const.cpp:10:8: note: previous definition of ‘struct my_is_const<const T>’
   10 | struct my_is_const<T const> : std::true_type
      |        ^~~~~~~~~~~~~~~~~~~~
my_is_const.cpp:14:1: error: multiple types in one declaration
   14 | struct my_is_const<const T> : std::true_type
      | ^~~~~~

从报错来看const T和T const 重复定义了。

对于普通类型来说,const放在类型前面和类型后是没有区别的,const只有在定义指针时放在前面和后面才有区别,再试着将代码修改一下:

#include <iostream>
#include <type_traits>
using namespace std;

template <class T>
struct my_is_const : std::false_type
{};

template <class T>
struct my_is_const<T const> : std::true_type
{};

template <class T>
struct my_is_const<const T*> : std::true_type
{};

int main()
{
    cout << "is_const:" << endl;
    cout << "int:" << my_is_const<int>::value << endl;
    cout << "const int:" << my_is_const<const int>::value << endl;
    cout << "int*:" << my_is_const<int*>::value << endl;
    cout << "const int*:" << my_is_const<const int*>::value << endl;
    cout << "int* const:" << my_is_const<int* const>::value << endl;
    cout << "float:" << my_is_const<float>::value << endl;
    cout << "const float:" << my_is_const<const float>::value << endl;
}

这次编译通过了,而且运行结果也如我们所预期的:

is_const:
int:0
const int:1
int*:0
const int*:1
int* const:1
float:0
const float:1

 

二、可变参数模板

上面判断的是类型的const,我们能否实现一个判断函数的const 呢? 

这里的关键就是模板的特化,我们怎么特化一个函数类型呢?

我们把函数的特征表示出来是否就是能特化一个函数类型呢?下面就来试验下

#include <iostream>
#include <type_traits>
using namespace std;

template <class R>
struct func_is_const : std::false_type
{};

template <class R>
struct func_is_const<const R(*)()> : std::true_type
{};

const int func();
char func2();
typedef const int (*func_type)();
typedef char (*func2_type)();
int main()
{
	cout << "const int func():" << func_is_const<func_type>::value << endl;
	cout << "char func2():" << func_is_const<func2_type>::value << endl;
	return 0;
} 

 在这段代码中,我们将模板特例化函数类型,在使用时将函数类型作为实参传入。运行发现,能正常工作,输出也如预期一样

const int func():1
char func2():0
 

在这里我们发现,在传入模板实参时,传入的是重命名的函数类型。这样比较麻烦,每一个函数我们都要重命名。

在C++11中增加了一个新的关键字 decltype, 用来在编译时推导出一个表达式的类型。我们用这个关键字来试着求函数的类型。

#include <iostream>
#include <type_traits>
using namespace std;

template <class R>
struct func_is_const : std::false_type
{};

template <class R>
struct func_is_const<const R(*)()> : std::true_type
{};

const int func();
char func2();
int main()
{
    cout << "const int func():" << func_is_const<decltype(func)>::value << endl;
    cout << "char func2():" << func_is_const<decltype(func2)>::value << endl;
    return 0;
}

 在运行这段代码时,发现其输出和我们的预期不一样:

const int func():0
char func2():0
 

这里的const int func()判定为false了,怎么会这样呢?

decltype能推导出一个表达式或变量的类型,那么函数名是否可以当成一个变量。变量都是有地址的,也就是说函数是否是一个地址。

函数名并不是函数地址的代表,这种误解与数组名就是指针一样犯了相同的错误。
函数名是函数实体的代表,不是地址的代表

 在网上找到这样一段话,这里说函数名不是地址,那么也就不能用decltype直接推导类型了。

我们用函数名取地址试试,这次发现是运行正常的。

 

函数由返回值和参数类型和参数个数组成,其中一个元素不一样,其函数的类型也就不一样,按照我们上面这样写,那不是要为每一个函数物例化一个模板类。

能否使用参数模板包含所有的函数类型呢?下面来试试

#include <iostream>
#include <type_traits>
using namespace std;

template <class R>
struct func_is_const : std::false_type
{};

template <class R>
struct func_is_const<const R(*)()> : std::true_type
{};

template <class R, class ...T>
struct func_is_const<const R(*)(T...)> : std::true_type
{};

const int func();
char func2();
const int func3(int);
int func4(int);
const char func5(int, char);
char func6(int, char);

int main()
{
    cout << "const int func():" << func_is_const<decltype(&func)>::value << endl;
    cout << "char func2():" << func_is_const<decltype(&func2)>::value << endl;
    cout << "const int func3(int):" << func_is_const<decltype(&func3)>::value << endl;
    cout << "int func4(int):" << func_is_const<decltype(&func4)>::value << endl;
    cout << "const char func5(int, char):" << func_is_const<decltype(&func5)>::value << endl;
    cout << "char func7(int, char):" << func_is_const<decltype(&func6)>::value << endl;
    return 0;
}

运行结果如预期一样,能匹配不同的参数个数和类型。

这里用到了模板特化和类型自动推导。

当我们在特化模板struct func_is_const<const R(*)(T...)>时,实际上我们在这里指定的模板参数的组织形式,当我们传入一个特定的函数类型时,编译器会根据实际的函数类型自动推导出模板参数中R T所表示的实际类型。当我们传入的函数类型没有匹配到特化的模板时,就会匹配到无特化的模板。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值