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++完美转发的话,会禁止推导{...}模板类型参数,因而不能用初化列表作为入参.常式限定指针时,表示指针本身为常量,而非所指对象常量,与不一样,不允许把常式放在类型声明表达式中间.因此,编译期计算常式指针指向的串长度,该串必须在静态数据区.不能位于栈/堆中,因为无法确定其地址.

posted @ 2020-12-01 21:59  zjh6  阅读(43)  评论(0)    收藏  举报  来源