C++traits

traits

C++的标准库提供了<type_traits>,它定义了一些编译时基于模板类的接口用于查询、修改类型的特征:输入的时类型,输出与该类型相关的属性

通过type_traits技术编译器可以回答一系列问题:它是否为数值类型?是否为函数对象?是不是指针?有没有构造函数?能不能拷贝构造等等

type_traits技术还能对类型进行变换,比如给定的类型任意T,能为这个类型添加const修饰符、添加引用或指针等。而这一切都发生在编译时,过程中没有任何运行时开销

判断类别

谓词命名以is_为前缀,通过访问静态成员常量value得到输出结果

cpp 复制代码
static_assert(std::is_integral<int>::value);//true
static_assert(! std::is_integral<float>::value);//false
static_assert(std::is_floating_point<double>::value);//true
static_assert(std::is_class<struct Point>::value);//true
static_assert(!std::is_same<int , long>::value);//false

is_integral用来判断给定的类型是否为整数类型,使用尖括号将类型输出给这个trait,通过其成员valu来输出一个bool类型的结果

is_floating_point用来判断给定的类型是否为浮点类型

is_class用来判断给定的类型是否为class、struct定义的类型

is_same用来判断给定的两个类型是否为相同类型


对于一个type traits谓词的结果,标准库约定使用value常量来存储,C++17为其预定义了一系列模板,::value的访问方式能够用_v来代替

cpp 复制代码
template <typename T>constexpr bool is_integral_v = is_integral<T>::value;
template <typename T>constexpr bool is_class_v = is_class<T>::value;

类型变换

标准库有些typed traits拥有修改类型的能力:基于已有类型应用修改得到新的类型,输出类型可以通过访问type类型成员函数得到结果。

类型修改不会原地修改输入的类型,而是产生新的类型以应用这些修改

cpp 复制代码
static_assert(is_same_v<typename std::remove_const<const int>::type , int>);
static_assert(is_same_v<typename std::remove_const<int>::type , int>);
static_assert(is_same_v<typename std::add_const<int>::type ,const int>);
static_assert(is_same_v<typename std::add_pointer<int **>::type , int ***>);
static_assert(is_same_v<typename std::decay<int[5][6]>::type , int(*)[6]>);

remove_const将输入的类型移除掉const修饰符并返回新的类型,如果不带const则不变

add_const将输入的类型添加const修饰符

add_pointer为输入的类型添加一级指针

decay语义为退化通过模拟函数或值语义传递时会使所有应用到的函数参数类型退化,若为引用那么应用将会去掉


在C++11引入类型别名using后typename decay<int5 6>::type可变为decay_t<int5 6>

下面是标准库定义的一些类型别名

cpp 复制代码
template<typename T>using remove_const_t = typename remove_const<T>::type;
template<typename T>using decay_t = typename decay<T>::type;

辅助类

辅助类integral_constant将值与对应的类型包裹起来,从而能够将值转化为类型,也能从类型转换回值,实现值与类型的一一映射关系

这个类的主要作用是作为一个容器,用于存储在编译时就可以确定的值,并且这个值的类型也是在编译期可知的。

cpp 复制代码
using Two = std::integral_constant<int,2>;
using Four = std::integral_constant<int,4>;
static_assert(Two::value * Two::value == Four::value);

Tow和Four为两个类型,分别对应2,4。使用integral_constant将值转换成类型后通过value静态成员常量从类型中得到值并进行计算


标准库对应布尔类型也提供了bool_constant,实现时仅仅是integral_constant的类型别名

cpp 复制代码
template<bool v>using bool_constant = integral_constant<bool , v>;

将这两个值映射成类型

cpp 复制代码
uaing true_type = integral_constant<bool , true>;
uaing false_type = integral_constant<bool , false>;

enable_if

enable_if常出现于SFINAE场景中,通过对模板函数,模板类中的类型进行谓词判断,使得程序能够选择合适的模板函数的重载版本或模板类的特化版本

enable_if可接受两个模板参数,第一个参数为bool类型的值,当条件为真时输出的类型成员type的结果为第二个模板参数,否则没有类型成员type

c 复制代码
int a;
typename std::enable_if<a == 1,int>::type *a;  //当a==1时,condition为true,那么type为int,所以会声明一个int* a;
typename std::enable_if<!(a == 1),double>::type *a;  //a !=1时,condition为true,那么type为double,所以会声明一个double* a;

enable_if用于函数模板中,典型应用是作为函数模板的返回类型

cpp 复制代码
template<typename T>
typename std::enable_if<(sizeof(T) > 2)>::type funceb()
{
    //....
}
nmsp2::funceb<int>();//void funceb(){}
nmsp2::funceb<char>();//error:未找到匹配的重载函数,条件不满足

//C++14出了这个等同上面
template<typename T>
std::enable_if_t<(sizeof(T) > 2),T> funceb()
{
    T myt = {};
    return myt;
}

nmsp2::funceb<int>();//int funceb(){}
//nmsp2::funceb<char>();

traits使用示例

  • 类型推断
cpp 复制代码
#include <iostream>
#include <type_traits>

template<typename T>
void print_type_trait(T value) {
    // 使用std::integral_constant作为编译时的常量表达式容器
    using TraitType = std::integral_constant<bool, std::is_integral<T>::value>;
    
    // 使用if constexpr语句在编译期决定是否打印信息
    if constexpr (TraitType::value) {
        std::cout << "The type of the variable is integral: " << typeid(T).name() << '\n';
    } else {
        std::cout << "The type of the variable is not integral: " << typeid(T).name() << '\n';
    }
}

int main() {
    int integer = 10;
    double floating = 3.14;

    print_type_trait(integer); // 输出:"The type of the variable is integral"
    print_type_trait(floating); // 输出:"The type of the variable is not integral"

    return 0;
}
  • 模板函数
cpp 复制代码
template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value>::type
print_sum(T a, T b) {
    std::cout << "Sum of arithmetic types: " << a + b << '\n';
}

template<typename T>
typename std::enable_if<!std::is_arithmetic<T>::value>::type
print_sum(T a, T b) {
    std::cout << "Sum operation not supported for non-arithmetic types.\n";
}

struct NonArithmetic {};

int main() {
    print_sum(3, 5); // 输出:"Sum of arithmetic types: 8"
    print_sum("Hello", "World"); // 不会被实例化,因为std::string不是算术类型
    print_sum(NonArithmetic{}, NonArithmetic{}); // 输出:"Sum operation not supported for non-arithmetic types."

    return 0;
}

相关推荐
银河技术15 分钟前
从脚本到 Operator:用 Go 构建可靠的微服务自动扩缩容控制器
开发语言·微服务·golang
GIS数据转换器2 小时前
地下空间综合管廊运营管理平台
大数据·开发语言·人工智能·经验分享·perl
草莓熊Lotso2 小时前
【C++ 基于微服务的即时通讯系统】etcd + brpc 从安装到服务注册发现全链路实战
网络·数据库·c++·redis·微服务·etcd
Wang's Blog7 小时前
Java 项目实战: 交友项目:登录链路回顾与今日任务地图
java·开发语言·状态模式·交友
砚底藏山河8 小时前
python量化:行情数据回溯
java·开发语言·python·金融·maven
今年下半年8 小时前
AI智能体项目实战记录(02):初始化 Python 项目
开发语言·人工智能·python·fastapi
Williamtym8 小时前
PickGo Lab实践:用浏览器、Python和MCP串起机器人搬运仿真
开发语言·python·机器人
DongQiShanRen8 小时前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
CS创新实验室8 小时前
Python 对象“死“了之后去了哪?——引用计数与分代垃圾回收
开发语言·jvm·python
quantdash_cc9 小时前
Pandas 处理分钟级股票行情:从原始数据清洗到 K 线聚合
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash