C++武艺修炼--基于C++17实现筛选元组不同类型

该项目是基于C++17实现筛选元组给入的不同类型。
假如让大家设计实现,输入一堆类型数据,如含有int、double、std::string、char、float、等等数据类型以及自定义数据类型,从中排除掉输入的类型,如int,需输出其他剩余类型。大家会怎么实现?
这里需要结合利用模板元编程及泛型编程的思维来实现。
首先确定思路,实现过程:
1、自定义元组。输出数据为元组,这样可以包含多种数据结构,但是直接利用std::tuple是显然不够的,因为std::tuple一旦确定具体数值,是后期不能修改的。所以这里可以自己封装自定义一个tuple,来实现所需要求。这样灵活性大大增强,可以根据自我需求随意增改功能,不至于受制于人。
2、确定输出元组。确定筛选过后的具体返回类型,这里肯定需要利用元编程在编译期来确认具体返回类型,运行期处理是达不到这样的需求的。
3、遍历输入元组并为输出元组赋值。最后需要遍历输入元组,筛选并给输出赋值。
这样就大功告成啦!!!哈哈,开心!!!
不对,我还没有具体实现呢,高兴的太早了哇(😂😂😂)。。。。。。
那么接下来,小小功能而已,准备拿捏😎😎😎。

在写模板编程,一定要写命名空间,这样不至于担心和std中的函数名字有冲突。这里的命名空间一致为FILETYPE,英文单词 filer 译为筛选的,FILETYPE 译为筛选类型。

1、自定义元组实现:

#include <iostream>
#include <type_traits>
#include <vector>
#include <list>
#include <functional>
#include <cxxabi.h>

///筛选类型
namespace FILETYPE
{

template<typename T, typename U = std::void_t<>>
struct HasMemFunc : std::false_type
{

};

template<typename T>
struct HasMemFunc<T, std::void_t<decltype (std::declval<T>().myfunc_MTL)>> : std::true_type
{

};

///自定义元组
//泛化版本
template<typename... Types>
class TuplE;

//特化版本1
template <typename First, typename... Others>
class TuplE<First, Others...>
{
public:
    First            first;
    TuplE<Others...> others;

public:
    //构造函数1,支持TuplE<int,float> mytuple;这种构造方式
    TuplE()
    {
    }
    //构造函数模板2,支持TuplE<int, float> mytuple(12, 23.5f); 构造方式
    template<typename C_First, typename... C_Others
             , typename = std::enable_if_t< !HasMemFunc<C_First>::value >>
    TuplE(C_First&& parf, C_Others&&... paro)
        :first(std::forward<C_First>(parf)), others(std::forward<C_Others>(paro)...)
    {
    }

public:
    TuplE(TuplE<First, Others...>& tmptpl) :
        first(tmptpl.first), others(tmptpl.others)
    {
    }

public:
    template<typename C_First, typename... C_Others>
    TuplE(TuplE<C_First, C_Others...>& tmptpl) :first(tmptpl.first), others(tmptpl.others)
    {
    }


public:
    void myfunc_MTL() {}
};
//特化版本2:空元组不需要存储任何内容,支持TuplE<> mytuple;这种构造方式
template<>
class TuplE<>
{
public:
    //构造函数
    TuplE()
    {
    }
    //拷贝构造函数
    TuplE(TuplE<>&)
    {
    }
};

//获取元组元素个数   //虽然后面没用到但是也写上吧
template<typename TP>
struct sizeTP;

template<typename... TE>
struct sizeTP<TuplE<TE...>>
{
    static inline const size_t value_s = sizeof... (TE);
};
template<>
struct sizeTP<TuplE<>>
{
    static inline const size_t value_s = 0;
};

2、确定输出元组的实现:

///处理元组返回类型(元处理)
//插入元素
template<typename TI, typename NewElem, typename TPLT,  bool is = std::is_same<TI, NewElem>::value>
class push_type;

template<typename TI, typename NewElem, typename... Elems>
class push_type<TI, NewElem, TuplE<Elems...>, false>
{
public:
    using type_t = TuplE<NewElem, Elems...>;
};
template<typename TI, typename NewElem, typename... Elems>
class push_type<TI, NewElem, TuplE<Elems...>, true>
{
public:
    using type_t = TuplE<Elems...>;
};

//移除首元素
template<typename TPLT>
class pop_front;

template<typename FirstE, typename... OtherE>
class pop_front<TuplE<FirstE, OtherE...>>
{
public:
    using type = TuplE<OtherE...>;
};

//获取首元素类型
template<typename TPLT>
class front;

template<typename FirstE, typename... OtherE>
class front<TuplE<FirstE, OtherE...>>
{
public:
    using type = FirstE;
};

//判断是否为空元组
template<typename TPLT>
class is_empty
{
public:
    static inline const bool value = false;
};

template<>
class is_empty<TuplE<>>
{
public:
    static inline const bool value = true;
};
//获取返回类型
//泛化版本
template<typename TI, typename TPLT, bool = is_empty<TPLT>::value>
class getNewType;

//特化版本1, 当tuplE中有元素时
template<typename TI, typename TPLT>
class getNewType<TI, TPLT, false>
{
private:
    using first_elem = typename front<TPLT>::type; //获取元组中第一个元素类型
    //促使递归的using,递归结束后,result_rec负责配合type来整理最终的结果
    using result_rec = typename getNewType<TI, typename pop_front<TPLT>::type >::type;
public:
    using type = typename push_type<TI, first_elem, result_rec>::type_t;
};

//特化版本2,当tuplE中没有元素(空)时
template<typename TI, typename TPLT>
class getNewType<TI, TPLT, true>
{
public:
    using type = TPLT; //其实此时的TPLT就是typelist<>
};

3、遍历输入元组并为输出元组赋值的实现:


///根据索引 返回处理元组对应的数据
template<int index>
class TPLGetIdxData
{
public:
    template <typename First, typename... Others>
    static auto& myget(TuplE<First, Others...>& tmptpl) //auto用的好
    {
        return TPLGetIdxData<index - 1>::myget(tmptpl.others);
    }
};

//特化版本
template<>
class TPLGetIdxData<0>
{
public:
    template <typename First, typename... Others>
    static auto& myget(TuplE<First, Others...>& tmptpl)
    {
        return tmptpl.first;
    }
};

///根据索引 返回处理元组对应的类型
template<int index, typename TPLT>
class TPLGetIdxType;

template<int index, typename First, typename... Others>
class TPLGetIdxType<index, TuplE<First, Others...>>
{
public:
    using type = typename TPLGetIdxType<index-1, TuplE<Others...>>::type;
};
template<typename First, typename... Others>
class TPLGetIdxType<0, TuplE<First, Others...>>
{
public:
    using type = First;
};

//template<int index>
//class TPLGetIdxType<index, TuplE<>>
//{
//public:
//    using type = void;
//};


//函数模板
template<int index, typename... Types>
inline auto& TuplEGetData(TuplE<Types...>& tmptpl)  //这里的auto& 处理特别巧妙 目的是处理引用值 俺就是个天才哇
{
    return (TPLGetIdxData<index>::myget(tmptpl));
}

///处理元组
template<typename TI, int iIndex, int iIndex_New, typename TP, typename NewTuple,
         int  iSize/* = sizeTP<TP>::value_s*/,
         bool is = std::is_same<TI,  typename TPLGetIdxType<iIndex, TP>::type>::value>
struct DoWithTuple;

//特化1  相同类型时的处理  且没有遍历到最后一元素
template<typename TI, int iIndex, int iIndex_New, typename TP, typename NewTuple, int  iSize>
struct DoWithTuple<TI, iIndex, iIndex_New, TP, NewTuple, iSize, true>
{
    void operator()(TP& TP_Data, NewTuple& newData)
    {
        DoWithTuple<TI, iIndex + 1, iIndex_New, TP, NewTuple, iSize>()(TP_Data, newData);
    }
};
//特化2  不同类型时的处理  且没有遍历到最后一元素
template<typename TI, int iIndex, int iIndex_New, typename TP, typename NewTuple, int  iSize>
struct DoWithTuple<TI, iIndex, iIndex_New, TP, NewTuple, iSize, false>
{
    void operator()(TP& TP_Data, NewTuple& newData)
    {
        ///不同则进行赋值
        //这里用移动拷贝/*std::move*/ 没有问题,更加优化,
    	//主要注掉是方便调试对比
        TuplEGetData<iIndex_New>(newData) = /*std::move*/(TuplEGetData<iIndex>(TP_Data));
        DoWithTuple<TI, iIndex +1, iIndex_New +1, TP, NewTuple, iSize>()(TP_Data, newData);
    }

};
//特化3  相同类型时的处理  且遍历到最后一元素
template<typename TI, int iIndex_New, typename TP, typename NewTuple, int  iSize>
struct DoWithTuple<TI, iSize, iIndex_New, TP, NewTuple, iSize, true>
{
    void operator()(TP& TP_Data, NewTuple& newData)
    {
    }
};
//特化4  不同类型时的处理  且遍历到最后一元素
template<typename TI, int iIndex_New, typename TP, typename NewTuple, int  iSize>
struct DoWithTuple<TI, iSize, iIndex_New, TP, NewTuple, iSize, false>
{
    void operator()(TP& TP_Data, NewTuple& newData)
    {
    	//这里用移动拷贝/*std::move*/ 没有问题,更加优化,
    	//主要注掉是方便调试对比  用到的话可以std::move打开
        TuplEGetData<iIndex_New>(newData) = /*std::move*/(TuplEGetData<iSize>(TP_Data));
    }
};

//获取不同类型  最终实现
template<typename TI, typename TP>
struct TYPEMAG_GetDiffType;

template<typename TI, typename ... Elems>
struct TYPEMAG_GetDiffType<TI, TuplE<Elems...>>
{
    using type = typename getNewType<TI, TuplE<Elems...>>::type;
    auto operator()(TuplE<Elems...>& tmptuple)
    {
        type NewTuple;
        DoWithTuple<TI, 0, 0, TuplE<Elems...>, type, sizeof... (Elems) - 1>()(tmptuple, NewTuple);
        return NewTuple;
    }
};

}//namespace FILETYPE 筛选类型  //这里是命名空间FILETYPE 的结束

这里就封装完了,那么接下来就是调用啦。(鬼知道我当时调了多久,一个小bug调了2小时,必须叨叨两句,要不然俺一直愤愤不平😭😭😭请添加图片描述
DoWithTuple中的四个特化,一开始传参用 TP&& TP_Data,用的右值引用,后来统一改成左值引用了,但是有一个没改,只改了三个,一直报错,编译不过。我就一直纳闷,还以为我写的特化有问题。我还纳闷了,写了这么久模板了,这怎么有问题哩。后来纠结了两个小时左右,终于发现了这个问题,MMD原来漏了一个,顿时口吐芬芳,大家适当脑补一下就行啦😂😂😂)。

接下来进行调用:

//自定义一个数据类型
struct stuAAA
{
    int iAAA = 99;
};

int main(int argc, char *argv[])
{
    stuAAA stuData;
    FILETYPE::TuplE<stuAAA, float, int, int, double, QString> fileTu(stuData, 234.5f, 100, 200, 3.14, "sss");
    //调用   //筛选排除int型  输出剩余类型及数值
    auto tupNew = FILETYPE::TYPEMAG_GetDiffType<int, FILETYPE::TuplE<stuAAA, float, int, int, double, QString>>()(fileTu);

    QString strType = QString(abi::__cxa_demangle(typeid(tupNew).name(),
                                              nullptr, nullptr, nullptr));
    std::cout << "------输出类型为:::" << strType.toStdString() << std::endl;
    return 0;
}

最终输出打印版结果正确。好啦,大功告成,小小功能,拿捏。不容易啊啊啊啊啊啊啊啊啊!!!
用断点调试看一下结果。对比fileTu 和tupNew, 类型和对应的值都么有问忒!!!
在这里插入图片描述

请添加图片描述

功能完成啦,本人有很多思维是来自于王建伟老师编著的《C++新经典模板与泛型编程》,这里强烈推荐一下,该书对于初学者特别特别友好。
读到这里差不多就可以啦,大家有哪些疑问可以留言,或者有其他想实现的功能,也可以留言,我会考虑一下,根据自身能力去实现。

下面就是俺的不正经时刻啦!!!

请添加图片描述

远山

望远山,山而不语,

沉首看向大地,

大地也不语。

闭着眼、游离,

我愿沉浸有梦里。

梦里有逝去的树,

曾庇护过童年的安逸。

给予的阴凉是影,

是光阴,是梦中的温暖,是蓝色

也是风。
/n

曾望远山,山曾不语,

于是便向山中走去。

后来只身远山,

山仍不语。

我愿做一棵树,

庇护那梦,

给予的阴凉不是影,

是沉淀,是延续的绽放,是青色

也是风。
/n

敌土在我脚下,

离我很近,

但却很远…


该写于2023.9.16
主要描写的是一在外北漂的年轻人回到故土上,回想起美好童年的种种,也思念起已故去的亲人(爷爷奶奶),还有就是北漂的种种。。。。。。
好好生活吧,有一句话是这样说的“美好的童年可以治愈人的一生”,我大概就是这样的。
注释一下:
远山:曾经小时候所梦想的大城市。
大地:此刻的故土。
逝去的树:已远去的亲人。
影:梦里的无影无踪。
蓝色:向往美好、童年温暖,蓝色是温馨的颜色。
风:流逝。
青色:指奋斗的颜色,比如青山,青云志等等。
不语:现实对人的回应。

好啦,感谢阅读,感谢特别感谢!!!
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值