跳转到内容

类型特征(type traits)有什么用?

为模板类型参数添加限制条件

如果不加限制条件,什么类型的变量都能传给函数,如果传入的变量不支持函数内的运算,则会编译出错,并且显示出来的出错信息可能和出错的真实原因相去甚远。

加了限制条件之后,如果传入的变量不满足条件同样会出错,但是给出的错误信息是预料之内的,更便于程序调试。

/*
* 编写函数 average(),计算两个数字的平均值
*
* 传递给函数的参数类型可以是任意数值类型 如 int, float, double, long 等
* 确保当传递数值类型之外的其他类型时会显式报错
*/
#include <iostream>
#include <type_traits>
template <typename T1, typename T2>
auto average(T1 x, T2 y) {
static_assert(std::is_arithmetic_v<T1> && std::is_arithmetic_v<T2>, "average: arguments must be arithmetic");
return (x + y) / 2;
}
int main() {
std::cout << average(1.0,2.0) << std::endl;
std::cout << average(1,5.0) << std::endl;
std::cout << average("hello","world") << std::endl; // error
}

根据参数类型的不同,进行不同的操作

下面这个例子中用到了转发引用T&& x

错误的初次尝试:

/*
* 编写函数 func(),判断参数类型是否为 int,如果是,打印一条消息;如果不是,打印另一条消息。
*/
#include <iostream>
#include <type_traits>
template<typename T>
void func(T&& x) {
if constexpr (std::is_same_v<T, int>) {
std::cout << x << " is an int\n";
} else {
std::cout << x << " is not an int\n";
}
}
int main() {
func(1);
int x{2};
func(x); // 与预期不符,接收到的 x 的类型为 int&,std::is_same_v<T, int>为 false
const int y{3};
func(y); // 与预期不符,接收到的 x 的类型为 const int&,std::is_same_v<T, int>为 false
}

改正:将std::is_same_v<T, int>改为std::is_same_v<std::remove_cvref_t<T>, int>

标准库提供的相关类型特征有:

  • std::remove_const,去除类型的 const
  • std::remove_reference,去除类型的 引用
  • std::remove_cvref,同时去除类型的 const、volatile 和 引用。“cvref”是constvolatile和 reference 的缩写。关于 volatile,见什么是 volatile 类型
  • std::remove_volatile,去除类型的 volatile

更优雅一点,可以使用using创建一个类型别名:

/*
* 编写函数 func(),判断参数类型是否为 int,如果是,打印一条消息;如果不是,打印另一条消息。
*/
#include <iostream>
#include <type_traits>
template<typename T>
void func(T&& x) {
using BaseType = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<BaseType, int>) {
std::cout << x << " is an int\n";
} else {
std::cout << x << " is not an int\n";
}
}
int main() {
func(1);
int x{2};
func(x);
const int y{3};
func(y);
}

参考