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