c++数组用法.
参考地址
为了提供原生数据类似功能/性能.元素直接放实例中,非堆上分配,编译期确定大小,编译器隐式声明构造/析构/赋值操作符.
自动推导数组大小
全局数组作配置参数,但得手动加上大小.
数组 g配置段={1,2,5,6,7,9,3,4};
数组<正32型>g配置段={1,2,5,6,7,9,3,4};
用函数模板代替类模板,c++函数模板允许部分推导.
元<型名 R,型名 P,整 N,整...I>
常式 数组<R,N>到数组实现(P(&a)[N],序列<I...>)无异{
中{{a[I]...}};
}
元<型名 T,整 N>
常式 动 至数组(T(&a)[N])无异
{
中 到数组实现<移常易<T>,T,N>(a,造序<N>{});
}
元<型名 R,型名 P,整 N,整...I>
常式 数组<R,N>到数组实现(P(&&a)[N],序列<I...>)无异
{
中{{移动(a[I])...}};
}
元<型名 T,整 N>
常式 动 至数组(T(&&a)[N])无异
{
中 到数组实现<移常易<T>,T,N>(移动(a),造序<N>{});
}
动 g配置段=至数组<整>({1,2,5,6,7,9,3,4});
但模板参数推导拒绝隐式转换初化列表元素.不行,
然后增加模板参数,让输入数组/返回元素用不同类型.添加:
元<型名 R,型名 P,整 N>
常式 动 到类型数组(P(&a)[N])无异
{
中 到数组实现<R,P,N>(a,造序<N>{});
}
元<型名 R,型名 P,整 N>
常式 动 到类型数组(P(&&a)[N])无异
{
中 到数组实现<R,P,N>(移动(a),造序<N>{});
}
这两个函数带3个模板参数,第1个为返回型.
动 g配置段=到类型数组<正32型>({1,2,5,6,7,9,3,4});
但溢出呢?
动 g_a=到类型数组<正8型>({256,-1});
编译期校验.
理想是:编译期生成数组自动校验.用模板参数.用数组元素作为模板的非类型参数.用动确定模板的非类型参数.
元<型名 T>
常式 空 检查整区间()无异{}
元<型名 T,动 M,动...N>
常式 空 检查整区间()无异
{
静断(!((数值极限<T>::最小()>=0)&&(M<0)));
静断((M>=数值极限<T>::最小())&&
(M<=数值极限<T>::最大()));
检查整区间<T,N...>();
}
元<型名 T,动...N>
常式 动 声明数组()无异{
检查整区间<T,N...>();
数组<T,型长...(N)>a{{静转<T>(N)...}};中 a;
};
用静断来校验.声明数组用法:
常式 动 a1=声明数组<正8型,1,2,3,4,255>();
静断(a1.大小()==5);
静断(a1[3]==4);
动 a2=声明数组<正8型,1,2,3,-1>();
动 a3=声明数组<正16型,1,2,3,65536>();
声明成模板参数了.而如果这样:
元<型名 T,T...N>
常式 动 声明数组()无异
在校验前已是T型,也可用正64型,整64型.而数组元素是自定义类型,可通过自定义构造函数来控制转换类型.
编译期生成数组
常式只能返回单个值,stl不能为编译期常量,原生数组退化为指针.返回数组引用也不行.
常式 整*函数()无异
{
整 a[]={1,2,3,4};中 a;
}
不行的.但标::数组既可作编译期常量,也可为函数返回值.因而作为编译期返回集合数据的首选.冒泡:
元<型名 T,整 N>
常式 数组<T,N>排序(常 数组<T,N>&数字)无异
{
数组<T,N>排序(数字);
对(整 i=0;i<N;++i){
对(整 j=N-1;j>i;--j){
如(排序[j]>=排序[j-1])下;
T t=排序[j];排序[j]=排序[j-1];
排序[j-1]=t;
}
}
中 排序;
}
整 主()
{
常式 数组<整,4>在前{4,2,3,1};
常式 数组<整,4>在后=排序(在前);
静断(在后[0]==1);静断(在后[1]==2);
静断(在后[2]==3);静断(在后[3]==4);中 0;
}
编译期排序,快排也可以.注意常式不能调用非常式(如,交换,排序).传入参数为常,不能原位排序,必须返回新数组.好处是:有数组越界等,编译会失败.编译期测试能提前拦截,编译通过,即测试通过.
自定义数组相等:
元<型名 T,型名 U,整 M,整 N>
常式 极 等于实现(常 T&左边,常 U&右边)
{
静断(M==N);
对(整 i=0;i<M;++i){
如(左边[i]!=右边[i])中 假;
}
中 真;
}
元<型名 T,型名 U>
常式 极 等于(常 T&左边,常 U&右边)
{
中 等于实现<T,U,大小(左边),大小(右边)>(左边,右边);
}
元<型名 T,型名 U,整 N>
常式 极 等于(常 T&左边,常 U(&右边)[N])
{
中 等于实现<T,常 U(&)[N],大小(左边),N>(左边,右边);
}
整 主(){
常式 数组<整,4>在前{4,2,3,1};
常式 数组<整,4>在后=排序(在前);
静断(等于(在后,{1,2,3,4}));
静断(!等于(在前,在后));中 0;
}
其中,标::大小能返回容器/数组大小,这里只允许编译期确定的大小.两个版本.因为c++禁止推导{...}这种初化列表字面量为模板参数类型,必须声明参数类型为数组.可同样生成其他编译期集合常量.如定长自然数序列:
元<型名 T,整 N>
常式 动 自然数()无异
{
数组<T,N>数组{0};
对(整 i=0;i<N;++i)数组[i]=i+1;中 数组;
}
整 主(){
常式 动 数组=自然数<正32型,5>();
静断(等于(数组,{1,2,3,4,5}));中 0;
}
这样先初化为0,再挨个指定没有直接返回指定值效率高,这里不能去掉,因为常式函数中,不允许定义未初化局部变量.编译期无所谓,但运行时调用常式就不好了.更好的方法是前面的转数组初化.可写通用数组生成器.接受函数对象为参数来生成数组,如生成奇数,兔子数列.
元<型名 T>
常式 T 奇数(整 i)无异{
中 i*2+1;
}
元<型名 T>
常式 T 斐波那契(整 i)无异{
如(i<=1)中 1;
中 斐波那契<T>(i-1)+斐波那契<T>(i-2);
}
元<型名 T,整 N,型名 F,整...I>
常式 数组<移常易<T>,N>生成数组实现(F f,序列<I...>)无异{
中{{f(I)...}};
}
元<整 N,型名 F,型名 T=调果型<F,整>>
常式 数组<T,N>生成数组(F f)无异{
中 生成数组实现<T,N>(f,造序<N>{});
}
整 主(){
常式 动 奇数=生成数组<5>(奇数<正8型>);
静断(等于(奇数,{1,3,5,7,9}));
常式 动 斐波列数=生成数组<5>(斐波那契<正32型>);
静断(等于(斐波列数,{1,1,2,3,5}));
常式 动 指定=生成数组<3>([](整 i){中 i+10;});
静断(等于(指定,{10,11,12}));中 0;
}
还可传λ函数作为参数来生成数组.但要求c++17.
截取子数组
就类似子串了.用λ作为生成函数.
元<整 N,型名 T>
常式 动 子数组(T&&t,整 基)无异
{
中 生成数组<N>([基,t=前向<T>(t)](整 i){中 t[基+i];});
}
元<整 N,型名 T,整 M>
常式 动 子数组(常 T(&t)[M],整 基)无异
{
中 生成数组<N>([基,&t](整 i){中 t[基+i];});
}
整 主()
{
常式 动 x=子数组<3>({1,2,3,4,5,6},2);
静断(等于(x,{3,4,5}));
常式 动 x1=子数组<2>(x,1);
静断(等于(x1,{4,5}));
常式 正8型 a[]={9,8,7,6,5};
常式 动 y=子数组<2>(a,3);
静断(等于(y,{6,5}));
常式 常 符*串="[!串000]";
常式 动 z=子数组<5>(串,6);
静断(等于(z,{'w','o','r','l','d'}));
向量<整32型>v{10,11,12,13,14};
整 n=2;动 d=子数组<3>(v,n);
断定(等于(d,{12,13,14}));中 0;
}
N为大小,基为开始,可为运行时,当然也可编译时.入参都是编译时时,生成的子数组也是编译时了.两个版本,可传入初化列表.还可以:
元<型名 T>
常式 动 总长(常 T&数组)无异{
中 大小(数组);
}
元<型名 P,型名...T>
常式 动 总长(常 P&p,常 T&...数组)无异{
中 大小(p)+总长(数组...);
}
元<型名 T>
常式 动 选取元素(整 i,常 T&数组)无异{
中 数组[i];
}
元<型名 P,型名...T>
常式 动 选取元素(整 i,常 P&p,常 T&...数组)无异
{
如(i<大小(p))中 p[i];
中 选取元素(i-大小(p),数组...);
}
元<型名...T>
常式 动 连接数组(常 T&...数组)无异{
中 生成数组<总长(数组...)>([&数组...](整 i){中 选取元素(i,数组...);});
}
整 主(){
常式 整32型 a[]={1,2,3};
常式 动 b=到类型数组<整32型>({4,5,6});
常式 动 c=声明数组<整32型,7,8>();
常式 动 x=连接数组(a,b,c);
静断(等于(x,{1,2,3,4,5,6,7,8}));中 0;
}
拼接.用模板参数兼容原生数组与数组.也可拼接自定义类型,但未适配初化列表,因而不能:
常式 动 x=连接数组(a,{4,5,6});
但可以先转数组.
常式 动 x=连接数组(a,至数组({4,5,6}));
编译期拼接字符串
原生这样:
符 串[]="abc";
调用数据方法,数组就会返回串针.
常式 动 串=至数组("abc");
静断(串.大小()==4);
静断(等于(串,"abc"));
输出<<串.数据();
串字面量为符[]类型,也能拼接,但结果不一样:
常式 动 串=连接数组("abc","定义");
静断(串.大小()==8);
静断(等于(串,{'a','b','c','\0','d','e','f','\0'}));
当然可去掉\0.
元<动 E>
常式 动 选取符(整 i){
中 E;
}
元<动 E,型名 P,型名...T>
常式 动 选取符(整 i,常 P&p,常 T&...数组){
如(i<(大小(p)-1)){
如(p[i]==E)抛"[!串001]";中 p[i];
}
如(p[大小(p)-1]!=E)抛"[!串002]";
中 选取符<E>(i-(大小(p)-1),数组...);
}
元<型名...T,动 E='\0'>
常式 动 连接串(常 T&...串){
中 生成数组<总长(串...)-型长...(T)+1>([&串...](整 i){
中 选取符<E>(i,串...);
});
}
整 主()
{
常式 符 a[]="[!串003]";
常式 动 b=至数组("[!串004]");
常式 动 串=连接串(a,b,"C++");
静断(等于(串,"[!串005]"));
中 0;
}
在其中,校验串长=容器长-1,编译期异常产生编译时错误,没必要运行时调用它,有人想编译期计算串长度:
元<型名 T,动 E='\0'>
常式 整 串长(常 T&串)无异{
整 i=0;当(串[i]!=E)++i;中 i;
}
常式 常 符*g串="abc";
整 主()
{
常式 动 串=子数组<串长(g串)+1>(g串,0);
静断(等于(串,"abc"));中 0;
}
然而,c++常式要求输入参数为常式时允许结果为常式,函数内部非常式.用万恶的宏?:
#定义 到数组串(x)子数组<串长(x)+1>(x,0)
常式 常 符*g串="abc";
整 主()
{
常式 动 串=连接串(到数组串(g串),"定义");
静断(等于(串,"6字母"));
中 0;
}
都可以传递指针类型,用变参宏还可以定义出按实际串长计算结果串长的更通用拼接函数.但我们只做编译期拼接.不搞那么复杂.
c++20:数组有常式版本比较符,函数可用常求值表明只在编译时调用.模板非类型可允许更多类型.stl容器,可作常式常量.这还只是小更新,万恶的c++!
注意:c++用完美转发的话,会禁止推导{...}为模板类型参数,因而不能用初化列表作为入参.常式限定指针时,表示指针本身为常量,而非所指对象为常量,与常不一样,不允许把常式放在类型声明表达式中间.因此,编译期计算常式指针指向的串长度,该串必须在静态数据区.不能位于栈/堆中,因为无法确定其地址.
浙公网安备 33010602011771号