1 type_traits 的属性类型特性
1.1 std::alignment_of
std::alignment_of 是一个模板类,用于获取类型的对齐要求。对齐是指数据在内存中的位置,某些硬件平台可能要求特定类型的数据位于特定地址的边界上。
定义:
template< class T >
struct alignment_of;
样例:
#include <iostream>
#include <type_traits>
int main() {
std::cout << "Alignment of int: " << std::alignment_of<int>::value << std::endl;
std::cout << "Alignment of double: " << std::alignment_of<double>::value << std::endl;
return 0;
}
上面代码输出为:
Alignment of int: 4
Alignment of double: 8
1.2 std::rank
std::rank 是一个模板类,用于获取数组类型的维度数。对于非数组类型,其值为 0。
定义:
template< class T >
struct rank;
样例:
#include <iostream>
#include <type_traits>
int main() {
std::cout << "Rank of int: " << std::rank<int>::value << std::endl;
std::cout << "Rank of int[3]: " << std::rank<int[3]>::value << std::endl;
std::cout << "Rank of int[3][4]: " << std::rank<int[3][4]>::value << std::endl;
return 0;
}
上面代码输出为:
Rank of int: 0
Rank of int[3]: 1
Rank of int[3][4]: 2
1.3 std::extent
std::extent 是一个模板类,用于获取数组类型的长度。它通常与 std::rank 结合使用,以确定特定维度的长度。
定义:
template< class _Ty, unsigned int _Nx = 0 >
struct extent;
样例:
#include <iostream>
#include <type_traits>
int main() {
std::cout << "Rank of int: " << std::rank<int>::value << std::endl;
std::cout << "Rank of int[3]: " << std::rank<int[3]>::value << std::endl;
std::cout << "Rank of int[3][4]: " << std::rank<int[3][4]>::value << std::endl;
return 0;
}
上面代码输出为:
Extent of int[3]: 3
First dimension extent of int[3][4]: 3
Second dimension extent of int[3][4]: 4
1.4 std::remove_all_extents
std::remove_all_extents 是一个模板类,用于移除数组类型的所有维度,将其转换为其元素类型。换句话说,它将多维数组转换为其基础元素类型。
定义:
template< class T >
struct remove_all_extents;
样例:
#include <iostream>
#include <type_traits>
int main() {
std::cout << typeid(std::remove_all_extents<int[3][4]>::type).name() << std::endl;
return 0;
}
上面代码输出为:
int
注意:typeid(T).name() 在不同的编译器和平台上可能会返回不同的字符串,因此上述输出可能会因环境而异。但核心思想是 std::remove_all_extents 将数组类型转换为其基础元素类型。
2 std::rank 与 std::extent 的综合应用示例
std::rank 和 std::extent 分别用于获取数组或指针类型的维度数和特定维度的长度。这两个工具在泛型编程和模板元编程中特别有用,尤其是在处理不同维度的数组或指针类型时。
下面是一个综合使用 std::rank 和 std::extent 的真实应用场景示例:一个函数模板,用于打印任意维度数组的形状(即各维度的长度)。
#include <iostream>
#include <type_traits>
// 递归辅助函数模板,用于打印数组形状
template <typename T, std::size_t N = std::rank<T>::value>
struct print_array_shape_helper;
// 特化:数组维度为0时,结束递归
template <typename T>
struct print_array_shape_helper<T, 0> {
static void print(const T&) {
// 无需打印,因为没有更多维度
}
};
// 递归情况:打印当前维度大小,并递归调用下一个维度
template <typename T, std::size_t N>
struct print_array_shape_helper {
static void print(const T& arr) {
std::cout << std::extent<T, N - 1>::value << " ";
print_array_shape_helper<T, N - 1>::print(arr);
}
};
// 打印数组形状的包装函数
template <typename T>
void print_array_shape(const T& arr) {
constexpr std::size_t rank = std::rank<T>::value;
if (rank > 0) {
std::cout << "Array shape: ";
print_array_shape_helper<T, rank>::print(arr);
std::cout << std::endl;
}
else {
std::cout << "Not an array type." << std::endl;
}
}
int main()
{
// 一维数组
int arr1[] = { 1, 2, 3, 4, 5 };
print_array_shape(arr1); // 输出: Array shape: 5
// 二维数组
int arr2[][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
print_array_shape(arr2); // 输出: Array shape: 3 3
// 不是数组的类型
int non_array = 42;
print_array_shape(non_array); // 输出: Not an array type.
return 0;
}
上面代码的输出为:
Array shape: 5
Array shape: 3 3
Not an array type.
在这个示例中,print_array_shape 函数模板接受一个数组作为参数,并使用 std::rank 来确定其维度数(即秩)。然后,它使用 std::make_index_sequence 生成一个从 0 到 rank - 1 的索引序列,并将其传递给 print_array_shape_impl 函数模板。
print_array_shape_impl 函数模板接受一个数组和一个索引序列作为参数。它使用折叠表达式(C++17及更高版本中的特性)和 std::extent 来遍历数组的每一个维度,并打印出每一维的长度。
通过这种方式,我们可以编写一个通用的函数来打印任意维度数组的形状,而无需事先知道数组的确切维度数。这种技术对于处理不同大小和维度的数组非常有用,尤其是在科学计算、数据分析或任何需要处理多维数据的领域中。
3 type_traits 的操作类型特性
3.1 std::conditional
std::conditional 是一个模板类,它接受一个布尔条件、两个类型 T 和 F 作为模板参数,然后根据条件在 T 和 F 之间选择一个类型。如果条件为 true,则选择 T,否则选择 F。
定义:
template<class _Ty1,
class _Ty2>
struct conditional<true, _Ty1, _Ty2>
样例:
#include <iostream>
#include <type_traits>
int main() {
using Type1 = std::conditional<true, int, double>::type; // Type1 是 int
using Type2 = std::conditional<false, int, double>::type; // Type2 是 double
std::cout << typeid(Type1).name() << std::endl;
std::cout << typeid(Type2).name() << std::endl;
return 0;
}
上面代码输出为:
int
double
3.2 std::common_type
std::common_type 是一个模板类,它接受多个类型作为模板参数,并确定这些类型的公共类型。如果所有类型都可以隐式转换为某个类型,那么 common_type 就是该类型。
定义:
template<class _Ty1,
class _Ty2,
class... _Rest>
struct common_type<_Ty1, _Ty2, _Rest...> : _Common_type3<void, _Ty1, _Ty2, _Rest...>
样例:
#include <iostream>
#include <type_traits>
int main() {
using CommonType = std::common_type<int, float, double>::type; // CommonType 是 double
std::cout << typeid(CommonType).name() << std::endl;
return 0;
}
上面代码输出为:
double
3.3 std::decay
std::decay 是一个模板类,它接受一个类型作为模板参数,并产生一个“衰减”后的类型。这通常意味着它会去除引用和 CV 限定符(const 和 volatile),如果是数组或函数类型,则会转换为对应的指针类型。
定义:
template<class T>
struct decay
样例:
#include <iostream>
#include <type_traits>
int main() {
using DecayedType = std::decay<int&>::type; // DecayedType 是 int
using DecayedArrayType = std::decay<int[5]>::type; // DecayedArrayType 是 int*
std::cout << typeid(DecayedType).name() << std::endl;
std::cout << typeid(DecayedArrayType).name() << std::endl;
return 0;
}
上面代码输出为:
int
int * __ptr64
3.4 std::enable_if
std::enable_if 是一个模板类,它根据一个布尔条件来启用或禁用模板特化。它通常与 std::is_same、std::is_integral 等类型特性检查模板结合使用,以在编译时根据类型特性进行条件编译。
定义:
template<bool _Test,
class _Ty = void>
struct enable_if
样例:
#include <iostream>
#include <type_traits>
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void foo(T) {
std::cout << "Integral type." << std::endl;
}
template<typename T, typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
void foo(T) {
std::cout << "Non-integral type." << std::endl;
}
int main() {
foo(12);
foo(3.14);
return 0;
}
上面代码输出为:
Integral type.
Non-integral type.
4 std::conditional 应用于类型包装器
下面是一个使用 std::conditional 的示例,展示了如何在模板中基于类型属性选择一个类类型或者类型别名:
#include <iostream>
#include <type_traits>
// 整数类型包装器
template<typename T>
struct IntegerWrapper {
T value;
IntegerWrapper(T v) : value(v) {}
void print() const {
std::cout << "Integer: " << value << std::endl;
}
};
// 浮点类型包装器
template<typename T>
struct FloatWrapper {
T value;
FloatWrapper(T v) : value(v) {}
void print() const {
std::cout << "Float: " << value << std::endl;
}
};
// 使用std::conditional根据类型属性选择包装器
template<typename T>
using WrapperType = typename std::conditional<
std::is_integral<T>::value,
IntegerWrapper<T>,
FloatWrapper<T>
>::type;
int main() {
// 使用WrapperType为整数类型选择包装器
WrapperType<int> intWrapper(12);
intWrapper.print(); // 输出 "Integer: 12"
// 使用WrapperType为浮点类型选择包装器
WrapperType<double> floatWrapper(3.14);
floatWrapper.print(); // 输出 "Float: 3.14"
return 0;
}
上面代码输出为:
Integer: 12
Float: 3.14
这个示例定义了两个模板包装器:IntegerWrapper 用于整数类型,FloatWrapper 用于浮点类型。然后,使用 std::conditional 来根据类型 T 是否为整数类型来选择相应的包装器。这里 std::conditional 有三个模板参数:第一个是一个布尔表达式,第二个是在表达式为 true 时选择的类型,第三个是在表达式为 false 时选择的类型。
WrapperType 是一个类型别名模板,它使用了 std::conditional 来选择适当的包装器类型。然后,在 main 函数中,分别创建了一个整数类型的包装器和一个浮点类型的包装器,并调用了它们的 print 方法。
5 std::enable_if 应用于类型安全的加法函数
下面示例是一个简单的类型安全的加法函数,它根据输入类型选择不同的加法实现:
#include <iostream>
#include <type_traits>
// 整数加法
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
add(T a, T b) {
return a + b;
}
// 浮点加法
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
add(T a, T b) {
return a + b;
}
// 通用加法包装器
template<typename T>
auto add_wrapper(T a, T b) -> decltype(add(a, b)) {
return add(a, b);
}
template<typename T, bool IsIntegral>
struct adder;
template<typename T>
struct adder<T, true> {
static T add(T a, T b) {
return a + b; // 整数加法实现
}
};
template<typename T>
struct adder<T, false> {
static T add(T a, T b) {
return a + b; // 浮点加法实现(假设T是浮点类型)
}
};
// 使用std::conditional来选择加法器实现
template<typename T>
T conditional_add(T a, T b) {
return adder<T, std::is_integral<T>::value>::add(a, b);
}
int main()
{
// 使用add_wrapper进行整数加法
std::cout << "5 + 3 (integers) = " << add_wrapper(5, 3) << std::endl;
// 使用add_wrapper进行浮点加法
std::cout << "5.0 + 3.0 (floats) = " << add_wrapper(5.0, 3.0) << std::endl;
// 使用conditional_add进行整数加法
std::cout << "5 + 3 (integers via conditional) = " << conditional_add(5, 3) << std::endl;
// 使用conditional_add进行浮点加法
std::cout << "5.0 + 3.0 (floats via conditional) = " << conditional_add(5.0, 3.0) << std::endl;
return 0;
}
上面代码输出为:
5 + 3 (integers) = 8
5.0 + 3.0 (floats) = 8
5 + 3 (integers via conditional) = 8
5.0 + 3.0 (floats via conditional) = 8
这个示例定义了两个重载的 add 函数模板,分别用于整数和浮点数的加法。然后创建了一个 add_wrapper 函数模板,它使用 decltype 和 enable_if 来根据参数类型选择正确的 add 函数。
这种基于条件的类型选择在模板元编程中非常常见,允许程序员编写更加通用和灵活的代码,同时保持类型安全。