2. enable_if的使用场景

背景

在元编程过程中,我们经常会使用enable_if这个特性,enable_if是基于C++中的SFINEA(Substitution failure is not anerror,中文直译即是“匹配失败不是错误”)实现的,SFINEA的意思是在实例化过程中,比如有三个模板,但凡能匹配到一个正确的,另外两个模板在实例化过程中即使报错,编译器也认为没问题。

enable_if

enable_if定义在头文件<type_traits>中,简单的实现如下:

template<bool B, class T = void>
struct enable_if {};
 
template<class T>
struct enable_if<true, T> { typedef T type; };

使用场景

场景一:类模板偏特化

简单来说,就是对类模板中的某个参数类型进行特化处理。具体的代码使用如下:

#include <iostream>
template<class T, class Enable = void>
class A {
 public:
  A() { std::cout << "primary template\r\n"; }
}; // primary template

template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
 public:
  A() { std::cout << "partial specialization\r\n"; }
}; // specialization for floating point types

int main() {
  std::shared_ptr<A<int>> a1 = std::make_shared<A<int>>();//primary
  auto a2 = std::make_shared<A<double>>();//specialization
}
场景二:函数参数(不建议这么写,可以参考这里写法)

考虑一个场景,我们写了软件统计景区一个月的人数,由于黄山和黑山客流量根本不是一个数量级,所以景区买的服务器也不一样,比如黄山的磁盘可以存64bit的大小,而黑山景区的磁盘只有16bit的大小,所以这个函数参数要支持uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t。如果写成<typename T>的话,部分粗心工作人员可能会传入小数,那么结果就完全炸裂,为此需要把T限制为std::is_integral::value == true, 所以enable_if的作用就是限制参数类型的,这样可以让代码在编译器就给出报错信息,避免运行时挂掉出错,有点类似于static_assert:

struct A{};

template<typename T>
struct Traits{
    static const bool is_basic = true;
};

template<>
struct Traits<A>{
    static const bool is_basic = false;
};

template<typename T>
void f(T a, typename user_enable_if<Traits<T>::is_basic, void>::type* dump= 0){
    cout<<"a basic type"<<endl;
}

template<typename T>
void f(T a, typename user_enable_if<!Traits<T>::is_basic, void>::type* dump= 0){
    cout<<"a class type"<<endl;
}
int main(){
    A a;
    f(1);//a basic type
    f(a);//a class type
}

需要注意的是下面写法会报错,因为出现了两个合适的模板,自己写的时候要注意:

template<typename T1, typename T2>
void show(T1 par_1, T2 par_2)
{
      std::cout<<par_1<<std::endl;
      std::cout<<par_2<<std::endl;
};

template<typename T1, typename T2, typename dummpy=std::enable_if_t<sizeof(T2)==8>>
void show(T1 par_1, T2 par_2)
{
        std::cout<<"enable if"<<std::endl;
};

在这里插入图片描述

场景三:函数返回值

和上面的使用类似

struct A{};

template<typename T>
struct Traits{
    static const bool is_basic = true;
};
template<>
struct Traits<A>{
    static const bool is_basic = false;
};
template<typename T>
typename user_enable_if<Traits<T>::is_basic, T>::type f(T a){
    cout<<"a basic type"<<endl;
    return a;
}
template<typename T>
typename user_enable_if<!Traits<T>::is_basic, T>::type f(T a){
    cout<<"a class type"<<endl;
    return a;
}

int main(){
    A a;
    f(1);
    f(a);
}

这里需要注意两问题,第一、struct中如果想用元模板编程,数值类型必须要用static修饰,不然没办法直接用A::is_basic来调取结果;第二、强烈建议是constexpr来代替const, 因为constexpr更加能说明是在编译器做的相关操作,这个值是一个编译期就确定的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
解析以下代码void Timer_Init(void) { //第一步开启时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE ); //使用TIM2需要使用APB1的开启时钟函数,因为TIM2是APB1总线的外设 //引脚要使用GPIO 需要需要配置GPIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_Initstructure; GPIO_Initstructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_Initstructure.GPIO_Pin=GPIO_Pin_0; GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_Initstructure); //第二步,选择时基单元的时钟,选择外部时钟 TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00); //单片机上单默认会选择内部时钟,所有这步可以省略 //第三步,配置时基单元 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period=10-1; TIM_TimeBaseInitStructure.TIM_Prescaler=1-1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM2,TIM_FLAG_Update); //第四步,使能更新中断 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启了更新中断到NVIC的通路 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; NVIC_Init(&NVIC_InitStructure); //最后一步,启动定时器 TIM_Cmd(TIM2,ENABLE); } uint16_t Timer_GetCounter(void) { return TIM_GetCounter(TIM2); } void TIM2_IRQHandler(void) { //首先要检测中断标志位 if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) { num++; //检测完要清除标准位 TIM_ClearITPendingBit(TIM2,TIM_IT_Update ); } }
05-29

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值