赞
踩
定义:
元编程(Metaprogramming)是指某类计算机程序的编写,这类计算机程序编写或者操纵其他程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作。(百度上的定义)
简单来说就是能在编译器处理一些程序,或进行一些运算就算元编程。
形式:
template <int x>
struct M
{
constexpr static int bval=x+1;
};
int main()
{
return M<5>::val; //编译器得到val=4
}
constexpr int fun(int x)
{
return x+1;
}
constexpr int val=fun(3); //编译器调用函数得到val=4
sizeof(int) //4
C++11引入元编程库,在#include<type_traits>中
例如:
介绍三种元程序代码的编写方法。
#include<type_traits>
template <typename T>
struct Fun
{
using RmRef=typename std::remove_reference<T>::type; //去引用
using type=typename std::add_const<T>::type; //去const
};
int main
{
Fun<int&>::type x=3;
}
template <typename T,unsigned S>
struct Fun
{
using RemRef=typename std::remove_reference<T>::type;
constexpr static bool value=(sizeof(RemRef)==S);
};
template <typename T,unsigned S>
using Fun_v=Fun<T,S>::value;
int main()
{
constexpr bool res=Fun<T,S>::value;
std::cout<<Fun_v<int,4><<std::endl; //类似is_same_v
}
constexpr int fun(int x) { if(x>2) { return x*2; } else { return x-100; } } int main() { constexpr int x=fun(100); } //或用数值模板参数 template <int x> int fun() { if constexpr(x>2) //必须有constexpr 否则运行期才进行分支运算 { return x*2; } else { return x-100; } }
template <int x>
struct Imp
{
constexpr static int value=x*2;
};
template <>
struct Imp<100>
{
constexpr static int value=100-3;
};
有的编译器不支持(clang支持,须引入C++2a)
template <int x> struct Imp; template <int x> requires(x<100) struct Imp <x> { ... ... }; template <int x> requires(x>=100) struct Imp <x> { ... ... }; int main() { constexpr auto x=Imp<100>::value; }
//conditional的可能实现 template<bool B,class T,class F> struct conditional { typedef T type; }; template <class T,class F> struct conditional<false,T,F> { typedef F type; }; template<bool B,class T,class F> using conditional_t=conditional<B,T,F>::type; int main() { using type1=std::conditional<true,int,double>::type//ture 时返回第一个模板类型,flase时返回第二个模板类型 }
应用场景受限,只能用于类型,有点像三元表达式
5. 基于SFINAE引入分支
SFINAE(匹配失败不是错误)
利用enable_if
可能实现:
template<bool B,class T=void>
struct enable_if{};
template <class T>
struct enable_if<true,T>
{
typedef T type;
};
template<bool B,class T=void>
using enable_if_t=enable_if<B,T>::type;
exanple1:
template<int x,std::enable_if_t<(x<100)>*=nullptr>
constexpr auto fun()
{
return x*2;
}
template<int x,std::enable_if_t<(x>=100)>*=nullptr>
constexpr auto fun()
{
return x-3; //匹配失败并非错误,当enable_if为false时不返回类型,不会报错,只是不匹配
}
int main()
{
constexpr auto x=fun<99>();
}
example2:
template<int x,typename=void> struct Imp; template<int x> struct Imp<x,std::enable_if_t<(x<100)> { constexpr static int value=x*2; }; template<int x> struct Imp<x,std::enable_if_t<(x>=100)> { constexpr static int value=x-3; }; int main() { constexpr auto x=Imp<99>::value; }
注意:不可这样编写,会导致函数重定义,模板函数不支持偏特化。
template<int x,std::enable_if_t<(x<100)>
constexpr auto fun()
{
return x*2;
}
template<int x,std::enable_if_t<(x>=100)>
constexpr auto fun()
{
return x-3;
}
利用void_t
(c++17引入)
可能实现:
template <class...>
using void_t=void;
example(代码来自cppreference):
template <typename T,typename=void>
struct is_iterable:std::false_type{}; //false_type包含一个成员value为false
template <typename T>
struct is_iterable<T,std::void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
:std::true_type{}; //true_type包含一个成员value为true
int main()
{
std::cout<<is_iterable<std::vector<double>>::value<<std::endl;//判断输入类型是否有begin和end迭代器
}
无begin和end迭代器时默认模板形参是void,调用第一个类模板(第二个有错)
有begin和end迭代器时默认模板形参是void,调用第二个类模板(第二个正确,完美匹配)。相当于除非void_t的条件不满足,否则一定调用第二个类模板。
用concept替换enable_if即可
7. 基于三元运算符引入分支
template <int x>
constexpr auto fun=(x<100)? x*2:x-3;
constexpr auto x=fun<99>;
一般基于递归来实现。
example1:获取一个数的二进制表示中1的个数
template <int x>
constexpr auto fun=(x%2)+fun<x/2>;
template <>
constexpr auto fun<0>=0; //递归停止
int main()
{
constexpr auto x=fun<99>
}
example2: 获取编译期数组中索引为奇数的所有元素
template <typename...>class Cont; using input=Cont<int,char,double,bool,void>; template <typename Res,typename Rem> struct Imp; template <typename... Processed,typename T1,typename T2,typename... TRemain> struct Imp<Cont<processed...>,Cont<T1,T2,TRemain>> { using type1=Cont<Processed... T1>; using type=typename Imp<type1,Cont<TRemain...>>::type; //typename 为防止歧义而引入 }; template <typename... Processed,typename T1> struct Imp<Cont<Processed...>,Cont<T1>> { using type=Cont<Processed...,T1>; }; template <typename... Processed> struct Imp<Cont<Processed...>,Cont<>> { using type=Cont<Processed...>; //由于输入数组可能为奇数也可能为偶数,故递归结束有种情况, //分别为剩一个元素和一个元素也不剩,分开讨论两种情况设置递归结束程序。 }; int main() { using output=Imp<Cont<>,input>::type; }
example3:打印编译期数组
template <unsigned... T> class Cont; //编译期数组 template <typename T> struct print { }; template <unsigned T1,unsigned... T> struct print<Cont<T1,T...>> { inline static void fun() { std::cout<<T1; print<Cont<T...>>::fun(); } }; template <> struct print<Cont<>> { inline static void fun() { } }; int main() { using input=Cont<1,2,3>; print<input>::fun(); }
example4:翻转编译器数组
template <unsigned... T> class Cont; template<typename T1,typename T2> struct reverse; template<unsigned... Process,unsigned T1,unsigned... Remain> struct reverse<Cont<Process...>,Cont<T1,Remain...>> { using type1=Cont<T1,Process...>; using type=typename reverse<type1,Cont<Remain...>>::type; }; template<unsigned... Process> struct reverse<Cont<Process...>,Cont<>> { using type=Cont<Process...>; }; int main() { using input=Cont<1,2,3>; using type1=reverse<Cont<>,input>::type; }
example5:编译器长整数数组相加,例如Cont<1,2,3>+Cont<2,3>=Cont<1,4,6>,使用了example3和4
整个程序流程为,先翻转数组,之后对每一位进行相加,得到最终结果输出。
template <unsigned T,typename=void> struct Add_Res; template <unsigned T> struct Add_Res<T,std::enable_if_t<(T<10)>> { constexpr static unsigned Carbit=0; constexpr static unsigned Res=T; }; template <unsigned T> struct Add_Res<T,std::enable_if_t<(T>=10)>> { constexpr static unsigned Carbit=1; constexpr static unsigned Res=T-10; }; /***************************************************************************************** * normal condition, the result of the bit is a+b+carry bit *****************************************************************************************/ template<typename result,typename Carbit,typename T1,typename T2> struct Add; template<unsigned... result,unsigned Carbit,unsigned T1,unsigned... Fnum,unsigned T2,unsigned... Snum> struct Add<Cont<result...>,Cont<Carbit>,Cont<T1,Fnum...>,Cont<T2,Snum...>> { constexpr static unsigned Nvalue=Add_Res<T1+T2+Carbit>::Res; constexpr static unsigned Ncarbit=Add_Res<T1+T2+Carbit>::Carbit; using last_type=typename Add<Cont<Nvalue,result...>,Cont<Ncarbit>,Cont<Fnum...>,Cont<Snum...>>::last_type; }; /***************************************************************************************** * b is null,the result of the bit is a+carry bit *****************************************************************************************/ template<unsigned... result,unsigned Carbit,unsigned T1,unsigned... Fnum> struct Add<Cont<result...>,Cont<Carbit>,Cont<T1,Fnum...>,Cont<>> { constexpr static unsigned Nvalue=Add_Res<T1+Carbit>::Res; constexpr static unsigned Ncarbit=Add_Res<T1+Carbit>::Carbit; using last_type=typename Add<Cont<Nvalue,result...>,Cont<Ncarbit>,Cont<Fnum...>,Cont<>>::last_type; }; /***************************************************************************************** * a is null,the result of the bit is b+carry bit *****************************************************************************************/ template<unsigned... result,unsigned Carbit,unsigned T2,unsigned... Snum> struct Add<Cont<result...>,Cont<Carbit>,Cont<>,Cont<T2,Snum...>> { constexpr static unsigned Nvalue=Add_Res<T2+Carbit>::Res; constexpr static unsigned Ncarbit=Add_Res<T2+Carbit>::Carbit; using last_type=typename Add<Cont<Nvalue,result...>,Cont<Ncarbit>,Cont<>,Cont<Snum...>>::last_type; }; /***************************************************************************************** * both a and b is null, stop the function iteration, return the result. *****************************************************************************************/ template<typename T1,typename T2> struct LastResult; template<unsigned... result,unsigned Carbit> struct Add<Cont<result...>,Cont<Carbit>,Cont<>,Cont<>> { using last_type=typename LastResult<Cont<Carbit>,Cont<result...>>::result; }; /***************************************************************************************** * if the carry_bit is 1 at last,we should put 1 at the begin of the result to get the last result. * For example 1+999=carry_bit:1 ,result_= 000 ->1000 *****************************************************************************************/ template<unsigned car_bit,unsigned...input> struct LastResult<Cont<car_bit>,Cont<input...>> { using result=std::conditional_t<car_bit,Cont<car_bit,input...>,Cont<input...>>; }; int main() { using type1=reverse<Cont<>,Cont<9,9,9>>::type; using type2=reverse<Cont<>,Cont<1>>::type; using t=Add<Cont<>,Cont<0>,type1,type2>::last_type; print<t>::fun(); }
example6:将m进制输入转换为n进制,如四进制的输入数组Cont<2,3>,转换为三进制的输出数组Cont<1,0,2>,程序中借用了example3和4
整个程序流程为,先翻转数组,之后转换为10进制数,再利用模n取余法逐步取余,得到最终结果输出。
constexpr unsigned m=4; constexpr unsigned n=3; /***************************************************************************************** * convert the input m_ary array to the decimal number *****************************************************************************************/ template <typename... T> struct DecimalRes; template <unsigned T1,unsigned... T> struct DecimalRes<Cont<T1,T...>> { constexpr static unsigned value=T1+m*DecimalRes<Cont<T...>>::value; }; template <> struct DecimalRes<Cont<>> { constexpr static unsigned value=0; }; /***************************************************************************************** * convert the decimal number to the n_ary *****************************************************************************************/ template <typename T1,typename T2> struct N_ary; template <unsigned remain,unsigned... result> //save the each compute result with parameter result struct N_ary<Cont<remain>,Cont<result...>> //parameter remain is the quotient of last step { constexpr static unsigned bit_value=remain%n; constexpr static unsigned remain_value=remain/n; using output=typename N_ary<Cont<remain_value>,Cont<bit_value,result...>>::output; }; template <unsigned... result> struct N_ary<Cont<0>,Cont<result...>> //stop the iteration of function when the parameter remain is 0 { using output=Cont<result...>; }; int main() { using input=reverse<Cont<>,Cont<2,3>>::type; //input array:Cont<2,3> 23(4) constexpr unsigned Dresult=DecimalRes<input>::value; //23(4) -> 11(10) using LastRes=N_ary<Cont<Dresult>,Cont<>>::output; //11(10) -->102(3) print<LastRes>::fun(); }
了解元编程的基本思想,熟悉常见三种程序的编写方式。
template<typename... T>
struct Cont;
利用偏特化或完全特化,在类模板的特化版本中
template <typename T>
struct print;
template <unsigned T1,unsigned... T>
struct print<Cont<T1,T...>>
{
constexpr static int x=T1;
};
如struct print<Cont<T1,T…>>
形如在模板中定义T::xxx或T.xxx,即在模板定义的内部,访问T的成员,则可能会引起歧义,必须加上template或typename进行说明。
例如:
using type=typename Imp<T1,T2>::type; 必须加上typename以说明type是一个类型。
引发歧义的原因,模板内使用了依赖性名称,其依赖于模板形参,如上,type间接依赖于T1,T2。必须声明type是一个类型,否则编译器认为type是一个静态数据成员,报错。
例如:
struct str { template <typename T> static void internal() { } } template <typename T> void fun() { T::internal<int>(); //错误 应该为 T::tempalte internal<int>();消除歧义 T obj; obj.internal<int>(); //错误 改为obj.tempalte internal<int>(); }
引发歧义的原因:inernal依赖于模板形参T,必须声明internal是一个函数模板,否则编译器会认为internal是一个成员,解析为internal<int导致错误
例如:
struct str
{
using internal=int;
}
template <typename T>
void fun()
{
T::internal*p; //错误 改为typename T::internal*p;
}
引发歧义的原因:inernal依赖于模板形参T,必须声明internal是一个类型,否则编译器会认为internal是一个静态数据成员,解析为internal*p导致错误
总结:
类内部数据成员选择constexpr static,函数内部数据成员选择constexpr,数组使用template<typename… T>
struct Cont来表示,在类模板内部使用using type=Cont。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。