跟我学C++中级篇——if constexpr的应用

227 篇文章 94 订阅

一、场景应用
在一个开发场景下,需要动态处理不同类型的数据写入。本来这个非常简单,只要定义一个模板即可搞定,但这里偏偏有一个细节,是调用别人的库来实现写入。而这个库对不同的数据类型的写入,提供了N种不同的函数,这些函数只是名字和写入数据类型不同,其它都完全一样。象下面的样子:

void putIntData(int *buf,int count,...);
void putCharData(char*buf,int count,...);
void putFloatData(float*buf,int count,...)
......

如果在上层应用调用这些函数,如下:

int * buf[1024] = {......};
int size = 1024;

void SaveData(int *buf,uint32_t size)
{
   if(std::is_same_v<decltype(buf[0]),int>)
   {
      putIntData(buf,size,...);
   }
   else if(std::is_same_v<decltype(buf[0]),char>)
   {
      putIntData(buf,size,...);
   }
   else
   {}
}

这样的话编译无法通过,会报类型转换的错误。可上层的给定的缓冲区内的数据类型确实是可变的,做为一个中间处理层,如何能够正确的引导程序自动适配准确的函数调用呢?很容易想到使用模板。但是单纯的使用模板,仍然会报上面的错误。这也提醒,应该在编译期处理这个逻辑,理论上就会没有问题了。

二、分析
既然使用编译期来定位函数的调用,首先想到的使用用模板的特化来处理:

template <typename T> bool SaveDataSecond(T *buf, int size) {
  SaveCharData(buf, size);
  return true;
}
template <> bool SaveDataSecond(int *buf, int size) {
  SaveIntData(buf, size);
  return true;
}
template <> bool SaveDataSecond(float *buf, int size) {
  SaveFloatData(buf, size);
  return true;
}
int main() {
  // template specialization
  SaveDataSecond(buf, size);
  SaveDataSecond(fbuf, size);
  return 0;
}

然后可以想到学过的SFINAE,先考虑一下SFINAE的实现,最先想到的是std::enable_if系列:

#include <iostream>

int buf[1024] = {0};
int size = 1024;

float fbuf[1024] = {0.f};

void SaveIntData(int *buf, int size, bool used = false) { std::cout << "save int type buffer!" << std::endl; }
void SaveCharData(char *buf, int size, bool used = false) { std::cout << "save char type buffer!" << std::endl; }
void SaveFloatData(float *buf, int size, bool used = false) { std::cout << "save float type buffer!" << std::endl; }

template <typename T> std::enable_if_t<std::is_integral<T>::value, bool> SaveData(T *buf, int size) {
  SaveIntData(buf, size);
  return 0;
}
template <typename T> std::enable_if_t<std::is_floating_point<T>::value, bool> SaveData(T *buf, int size) {
  SaveFloatData(buf, size);
  return 0;
}
// void SaveData(int *buf, int size) {}
int main() {
  SaveData(buf, size);
  SaveData(fbuf, size);
  return 0;
}

看到这些代码是不是想到了std::is_same系列,即把is_floating_point等替换为std::is_same,如下:

template <typename T> std::enable_if_t<std::is_same_v<T, float>, bool> SaveData(T *buf, int size) {
  SaveFloatData(buf, size);
  return 0;
}

然后就是考虑一下在C++高版本(C++17及以后)有没有更好的解决办法即C++17后的if constexpr,如下面的代码用法:

   if constexpr(std::is_same<T,int>::value)
   {
      //call switch
   }

三、解决
最后的解决办法就是使用if constexpr在编译期处理函数的分支调用:

int * buf[1024] = {......};
int size = 1024;

template<typename T>
void SaveData(T*buf,uint32_t size)
{
   if constexpr(std::is_same_v<T,int>)
   {
      putIntData(buf,size,...);
   }
   else if constexpr(std::is_same_v<T,char>)
   {
      putIntData(buf,size,...);
   }
   else
   {}
}

这种方法实现起来既简单又容易理解,类似的问题,都可以使用这种方式来处理。

四、总结
技术的前进一般是迭代推进的。完全全新的知识点,往往很少。要关于对老的知识点的综合应用并不断的总结这种用法的可用之处,从而不断的推导出问题的解决办法。再通过这种解决办法可以看新的标准中是否有类似的更方便的方法,就可以更好的理解和认知一些技术点。从而可以更好的更深入的掌握它。与诸君共勉!

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值