类型萃取即对类型进行操作,例如获取类型的名称、去除类型的修饰符、增加“类型”的修饰符。c++11的头文件#include <type_traits>中提供了很多的类模板帮助我们对类型进行萃取。下面对一些常见的类型萃取进行说明。

常量的萃取

integral_constant

用于获取某个类型静态常量,声明如下:

template<class T, T v>
struct integral_constant;

这里额外提供了两个bool的常量值std::true_typestd::false_type,标准库中的声明如下,这两个常量对于其他萃取器的实现有非常大作用:

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

类型种类的萃取

这种萃取能帮助我们判断一个类型是否是整形还是浮点型等。

is_integral

判断一个类型是否是整形,声明如下:

template<class T>
struct is_integral;

简单的使用:

// std::is_integral<T>::value
std::cout << "int = int: " << std::is_integral<int>::value << std::endl;

可以利用模板的偏特化实现一个简单版本:

#include <iostream>
#include <type_traits>

// 主模板
template<class T>
struct IsIntegral : std::false_type {};

// 特化模板
template<>
struct IsIntegral<int> : std::true_type {};

template<>
struct IsIntegral<long> : std::true_type {};

template<>
struct IsIntegral<short> : std::true_type {};

template<>
struct IsIntegral<unsigned int> : std::true_type {};

int main() {

std::cout << "int : " << IsIntegral<int>::value << std::endl;
std::cout << "long : " << IsIntegral<long>::value << std::endl;
std::cout << "float : " << IsIntegral<float>::value << std::endl;

return 0;
}

为了方便使用,c++17新增了对应的变量模板,拥有成员变量value的萃取类一般都提供了变量模板:

template<class T>
inline constexpr bool is_integral_v = is_integral<T>::value;

// std::is_integral_v<T>

下面是一些主要的类型种类萃取。

类模板 用途
is_void 检查类型是否是void
is_array 检查类型是否是array
is_enum 检查类型是否是enum
is_union 检查类型是否是union
is_class 检查类型是否是class
is_pointer 检查类型是否是指针类型
is_function 检查类型是否是函数类型
is_lvalue_reference 检查类型是否是左值引用
is_rvalue_reference 检查类型是否是右值引用
is_member_object_pointer 检查类型是否是指向非静态成员对象的指针
is_member_function_pointer 检查类型是否是指向非静态成员函数的指针

类型属性的萃取

这种萃取能帮助我们判断一个类型是否拥有某些属性。

is_const

判断一个类型是否是const限定的,声明如下:

template<class T>
struct is_const;

可以利用偏特化实现,标准库中的实现:

template<typename>
struct is_const : public false_type {};

template<typename _Tp>
struct is_const<_Tp const> : public true_type {};

类模板 用途
is_volatile 检查类型是否是volatile限定的
is_abstract 检查是否是抽象类类型

某类属性增删的萃取

remove_const

去除类型的const属性,声明如下:

template< class T >
struct remove_const;

标准库中的实现:

template<typename _Tp>
struct remove_const { typedef _Tp type; };

template<typename _Tp>
struct remove_const<_Tp const> { typedef _Tp type; };

c++11中的使用:

std::remove_const<T>::type;

为了简化使用,在c++14中提供了对应的别名模板拥有type类型的萃取类一般都提供别名模板

template< class T >
using remove_const_t = typename remove_const<T>::type;

std::remove_const_t<T>;

相应的remove_volatileremove_cv与之类似。

add_volatile

给类型增加volatile属性,声明如下:

template< class T >
struct add_volatile;

标准库中的实现:

template<typename _Tp>
struct add_volatile { typedef _Tp volatile type; };

相应的add_constadd_cv与之类似。

类模板 用途
remove_const 去除类型的const属性
remove_volatile 去除类型的volatile属性
remove_cv 去除类型的constvolatile属性
remove_reference 去除给定类型的引用,包括左值引用和右值引用
add_const 给类型增加const属性
add_volatile 给类型增加volatile属性
add_cv 给类型增加constvolatile属性
remove_pointer 去除类型的指针属性
add_pointer 给一个类型增加指针的属性

类型间关系的萃取

is_same

判断两个类型是否相同,声明如下:

template<class T, class U>
struct is_same;

标准库中的实现:

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

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

简单的使用:

// c++11
std::is_same<T, U>::value;
// c++17提供的变量模板
std::is_same_v<T, U>;

is_base_of

检查一个类型是否派生于其他类型,声明如下:

template<class Base, class Derived>
struct is_base_of;

is_convertible

检查一个类型是否能转换成另一个类型,声明如下:

template<class From, class To>
struct is_convertible;

简单的使用:

// c++14
std::is_convertible_v<char, int>;

is_invocable(c++17)

检查给定的函数、函数对象、函数指针或成员函数指针是否可以用给定的参数类型进行调用,声明如下:

template<class Fn, class... ArgTypes>
struct is_invocable;

类模板 用途
is_same 检查两个类型是否相同
is_base_of 检查一个类型是否派生于其他类型
is_convertible 检查一个类型是否能转换成其他类型
is_invocable 检查给定的函数、函数对象、函数指针或成员函数指针是否可以用给定的参数类型进行调用

支持某类操作的萃取

is_default_constructible

检查类型是否拥有默认构造函数,声明如下:

template< class T >
struct is_default_constructible;

简单的使用:

// c++11
std::is_default_constructible<T>::value;
// c++17
std::is_default_constructible_v<T>;

可以利用SFINAE特性排除偏特化的方式来实现,可能的实现方式:

#include <iostream>
#include <type_traits>

// SFINAE排除偏特化
template<class T, class = std::void_t<>>
struct IsDefaultConstructible : public std::false_type {};

template<class T>
struct IsDefaultConstructible<T, std::void_t<decltype(T())>> : public std::true_type {};

struct S {
S() = delete;
};

int main() {

std::cout << IsDefaultConstructible<int>::value << std::endl;;
std::cout << IsDefaultConstructible<S>::value << std::endl;

return 0;
}

类模板 用途
is_default_constructible 检查类型是否拥有默认构造函数
is_constructible 检查类型是否拥有构造函数
is_copy_constructible 检查类型是否拥有拷贝构造函数
has_virtual_destructor 检查类型是否拥有虚析构函数

各种变化的萃取

decay

能去除constvolatile的修饰,去除引用,将数组类型转换成指针,将函数类型转换成函数指针类型。声明如下:

template<class T>
struct decay;

简单的使用:

// c++11
std::decay<T>::type;
// c++14
std::decay_t<T>;

decay可能的简单实现如下:

#include <iostream>
#include <type_traits>

// remove const volatile
template<class T>
struct DecayT : std::remove_cv<T> {};

// 数组退化成指针
template<class T>
struct DecayT<T[]> {
using type = T*;
};

template<class T, size_t N>
struct DecayT<T[N]> {
using type = T*;
};

// 函数类型退化成函数指针
template<class F, class...Args>
struct DecayT<F(Args...)> {
using type = F(*)(Args...);
};

template<class T>
void print() {
using type = typename DecayT<T>::type;
if(std::is_same_v<type, int>){
std::cout << "type: int" << std::endl;
}else if(std::is_const_v<type>){
std::cout << "type: const" << std::endl;
}else if(std::is_pointer_v<type>){
std::cout << "type: pointer" << std::endl;
}else{
std::cout << "aaa" << std::endl;
}
}

int main() {
print<int>();
print<const int>();
print<const volatile int>();
print<int[]>();
print<int(int)>();
return 0;
}

void_t

能够检测出应用SFINAE特性时出现的非法类型。传入到std::void_t中的类型必须是一个有效的类型。定义如下:

template< class... >
using void_t = void;

用来探测类变量的实例:

#include <iostream>
#include <type_traits>

template<class T, class U = std::void_t<>>
struct HasValue : std::false_type {};

template<class T>
struct HasValue<T, std::void_t<decltype(T::value)>> : std::true_type {};

struct T {
static int value;
};

int main() {
std::cout << HasValue<int>::value << std::endl;
std::cout << HasValue<T>::value << std::endl;
return 0;
}

enable_if

有条件的从重载解析中删除函数重载和模板特化,声明如下:

template<bool B, class T = void>
struct enable_if;

只有当判断条件为true时,enable_if<...>::type才会存在,type类型和T保持一致,默认是void

简单的使用:

#include <iostream>
#include <type_traits>

template<class T, class Enable = void>
class A;

// 偏特化,限定第一个参数只能是浮点型;注意这里没有对A进行定义。
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {};

int main() {

A<float> a;
// A<int> b; // error

return 0;
}

conditional

条件选择萃取类型,声明如下:

template<bool B, class T, class F>
struct conditional;

如果Btrue,则conditional<...>::type的类型是T,否则就是F

简单的使用:

#include <iostream>
#include <type_traits>

int main() {

std::conditional<true, int, float>::type a = 1;
std::conditional<false, int, float>::type b = 2.1;

return 0;
}

invoke_result

获取可调用对象输入一组参数后返回结果的类型,声明如下:

template<class F, class... ArgTypes>
class invoke_result;

有一个类模板result_of也是差不多的效果,不过在c++17就不推荐使用了。在c++20的时候又增加了一个函数模板invoke对类模板invoke_result进行简单的封装,声明如下:

template<class F, class... Args>
constexpr std::invoke_result_t<F, Args...>
invoke(F&& f, Args&&... args) noexcept;

参考