C++20之模板元条件判断,编译期if else, switch case

#include <iostream>
#include "common/log.h"
#include <limits>
#include <vector>
#include <span>
#include <array>
#include <type_traits>
#include <cmath>
using namespace AdsonLib;
//模板元编程中的条件判断
//模板特化, SFINAE(enable_if),标签分发(tag dispatch), if constexpr, std::contional
//良构判断void_t:比如判断有没有Init函数, T::Init.编译不会失败

//is_floating_point实现
template<typename T>
struct is_floating_point : std::false_type{}; //默认false
//特化float类型
template<>
struct is_floating_point<float> : std::true_type{};
template<>
struct is_floating_point<double> : std::true_type{};
template<>
struct is_floating_point<long double> : std::true_type{};
void test_floating_point(){
    static_assert(is_floating_point<float>::value);
    static_assert(is_floating_point<double>::value);
    static_assert(is_floating_point<long double>::value);
    static_assert(std::is_floating_point<long double>::value);
}

//std::is_same实现:判断类型T和U是不是同一个类型
template<typename T, typename U>
struct is_same : std::false_type{};
template<typename T>
struct is_same<T, T> : std::true_type{};
void test_is_same(){
    static_assert(is_same<int, int>::value);
    static_assert(!is_same<int, float>::value);
    static_assert(std::is_same_v<int, int>);
}


//remove const实现
template<typename T>
struct remove_const {
    using type = T;
};
template<typename T>
struct remove_const<const T> {
    using type = T;
};
void test_remove_const(){
    static_assert(std::is_same_v<remove_const<const int>::type, int>);
}

//std::conditional实现 b为true返回类型T,否则返回类型U
template<bool b, typename T, typename U>
struct conditional {
    using type = T;
};
template<typename T, typename U>
struct conditional<false, T, U> {
    using type = U;
};
template<bool b, typename T, typename U>
using conditional_t = conditional<b, T, U>::type;
void test_conditional(){
    static_assert(std::is_same_v<std::conditional_t<true, int, float>, int>);
    static_assert(std::is_same_v<std::conditional_t<false, int, float>, float>);
    static_assert(std::is_same_v<conditional<true, int, float>::type, int>);
    static_assert(std::is_same_v<conditional<false, int, float>::type, float>);
    static_assert(std::is_same_v<conditional_t<false, int, double>, double>);
}

// 类型内省提取数组大小,函数参数返回值类型等
template<typename T> struct array_size;
template<typename T, int N> 
struct array_size<T[N]>{
    using type = T;
    static constexpr size_t len = N;
};
void test_array_size(){
    static_assert(array_size<int[5]>::len == 5);
    static_assert(std::is_same_v<array_size<int[5]>::type, int>);
    int a[6];
    using T = decltype(a);
    static_assert(std::is_same_v<array_size<T>::type, int>);
    static_assert(array_size<T>::len == 6);
}

//std::enable_if 实现
template<bool, typename T = void>
struct enable_if {};

template<typename T>
struct enable_if<true, T> {
    using type = T;
};

//当不是数值类型时,enable_if里没有::type,编译错误,根据SFINAE,跳过这个
template<typename T, enable_if<std::is_integral_v<T>, void>::type * = nullptr>
bool NumEQ(T &&a, T &&b) {
    return a == b;
}

template<typename T, enable_if<std::is_floating_point_v<T>, void>::type * = nullptr>
bool NumEQ(T &&a, T &&b) {
    static constexpr double epsilon = 1e-5;
    return std::fabs(b - a) < epsilon;
}
void test_num_eq(){
    LOG(INFO) << NumEQ(3, 3);
    LOG(INFO) << NumEQ(3, 4);
    LOG(INFO) << NumEQ(3.3, 3.3);
    LOG(INFO) << NumEQ(3.3, 3.31);
}

//标签分发:就像switch case. 比if else嵌套更直观一些
//定义标签
struct FloatType{};
struct OtherNumType{};
struct StringType{};

template<typename T>
struct NumCategory{
    using type = std::enable_if_t<std::is_integral_v<T>, OtherNumType> ;
};
template<> struct NumCategory<std::string>{using type = StringType;};
template<> struct NumCategory<float>{using type = FloatType;};
template<> struct NumCategory<double>{using type = FloatType;};
template<> struct NumCategory<long double>{using type = FloatType;};


//函数不能偏特化,所以用参数重载来分发
template<typename T>
bool NumEQ1(T &&a, T &&b, OtherNumType) {
    return a == b;
}
template<typename T>
bool NumEQ1(T &&a, T &&b, FloatType) {
    static constexpr double epsilon = 1e-5;
    return std::fabs(b - a) < epsilon;
}
template<typename T>
bool NumEQ1(T &&a, T &&b, StringType) {
    return a.size() == b.size();
}
template<typename T>
bool NumEQTag(T &&a, T &&b) {
    //这里还是嵌套了
    using U = conditional<std::is_floating_point_v<T>, 
                                 FloatType,
                                 typename conditional<std::is_integral_v<T>, OtherNumType, StringType>::type>::type;
    return NumEQ1<T> (std::forward<T>(a), std::forward<T>(b), U{});
}
template<typename T>
bool NumEQTag2(T &&a, T &&b) {
    //这里还是嵌套了
    using U = NumCategory<T>::type;
    return NumEQ1<T> (std::forward<T>(a), std::forward<T>(b), U{});
}
void test_num_eq1(){
    LOG(INFO) << "test num eq1";
    LOG(INFO) << NumEQTag(3, 3);
    LOG(INFO) << NumEQTag(3, 4);
    LOG(INFO) << NumEQTag(3.3, 3.3);
    LOG(INFO) << NumEQTag(3.3, 3.31);
    LOG(INFO) << NumEQTag(std::string("abc"), std::string("def"));
}
void test_num_tag2(){
    LOG(INFO) << "test num eq2";
    LOG(INFO) << NumEQTag2(3, 3);
    LOG(INFO) << NumEQTag2(3, 4);
    LOG(INFO) << NumEQTag2(3.3, 3.3);
    LOG(INFO) << NumEQTag2(3.3, 3.31);
    LOG(INFO) << NumEQTag2(std::string("abc"), std::string("def"));
}

//使用if constexpr来实现
template<typename T>
bool num_eq(T &&a, T &&b) {
    if constexpr(std::is_floating_point_v<T>){
        static constexpr double epsilon = 1e-5;
        return std::fabs(b - a) < epsilon;
    }else if constexpr(std::is_same_v<T, std::string>) { //if constexpr和if是不同的。if无论如何要生成代码。 if constexpr false是不生成代码的
        return  a.size() == b.size(); //不用if constexpr这里报错,因为当T=int时,这里还是要生成代码
    }else if constexpr(std::is_integral_v<T>){
        return a == b;
    }else{
        return a == b;
    }
}
    
void test_num_if(){
    LOG(INFO) << "test num";
    LOG(INFO) << num_eq(3, 3);
    LOG(INFO) << num_eq(3, 4);
    LOG(INFO) << num_eq(3.3, 3.3);
    LOG(INFO) << num_eq(3.3, 3.31);
    LOG(INFO) << num_eq(std::string("abc"), std::string("def"));
}


//良构判断std::void_t实现
template<typename...> using void_t = void;

//判断T中有没有type,即T::type
template<typename T, typename = void>
struct HasTypeMember : std::false_type{};

template<typename T>
struct HasTypeMember<T, void_t<typename T::type>> : std::true_type{};

//判断T中有没有Init方法
template<typename T, typename = void>
struct HasInit: std::false_type{};

template<typename T>
struct HasInit<T, std::void_t<decltype(std::declval<T>().Init())>>: std::true_type{};

struct FooInt{};
struct Bar{
    using type = int;
    void Init(){}
};
void test_has_type(){
    static_assert(!HasTypeMember<FooInt>::value);
    static_assert(HasTypeMember<Bar>::value);
    static_assert(HasTypeMember<std::false_type>::value);
    static_assert(!HasInit<FooInt>::value);
    static_assert(HasInit<Bar>::value);
}
int main(int argc, char *argv[]) {
    test_num_eq();
    test_num_eq1();
    test_num_tag2();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值