专栏文章
从 C++98 到 C++20,寻觅甜甜的语法糖们
科技·工程参与者 55已保存评论 60
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 60 条
- 当前快照
- 1 份
- 快照标识符
- @mhz5t2zt
- 此快照首次捕获于
- 2025/11/15 01:56 4 个月前
- 此快照最后确认于
- 2025/11/29 05:25 3 个月前
从 C++98 到 C++20,寻觅甜甜的语法糖们
谔谔,谔谔……
于是一时兴起,写了这么一篇博客,记录一些比较平时比较少见的 / 冷门的 / 超前的语法知识,供大家学习参考。
本人水平有限,若有遗漏或错误请大家毫不吝啬地指出,谢谢!
- 选取 OI 中可能用到的,但比较冷门的语法、函数。
- 以版本为线进行了梳理(日后有时间将会以其它角度再写一些内容)。
- 实用至上,尽量少地探究语法背后的本质。
- 将不断更新,完善。
如果不确定能不能用,请见 《关于NOI系列活动中编程语言使用限制的补充说明》
upd on 2024/1/17: for saving
目录
C++98
-
algorithm 库
- 一些常见且比较重要的函数
- 一些比较冷门的函数
- GNU 私货
-
numeric 库
-
functional 库
-
cmath 库
-
__builtin家族
C++11
- 零零散散的语法糖
- auto
- lambda 表达式
- range-for
- STL
- emplace
- shrink_to_fit
- algorithm 库
- numeric 库
- iterator 库
- functional 库
- random 库
- cmath 库
C++14
- 零零散散的语法糖
- auto
- functional 库
C++17
- 零零散散的语法糖
- 结构化绑定
- 类模板的实参推导
- if 和 switch 中进行初始化
- using 多个名称
- STL
- set/map 的 merge
- algorithm 库
- numeric 库
- iterator 库
- cmath 库
C++20
- 零零散散的语法糖
- 三路比较运算符
- range-for 的初始化语句和初始化器
- 规定有符号整数以补码实现
- STL
- bit 库
- ranges 库
- numbers 库
C++ 98
algorithm 库
algorithm 中有大量的函数,这些函数都位于命名空间 std 中。最常见的有
max/min、swap、sort、unique、lower_bound/upper_bound、reverse。一些常见且比较重要的函数
-
find(bg,ed,val)- 返回指向第一个等于 的元素的指针。
- 时间复杂度 。后文所有序列操作的函数,都以 代指 。
-
fill(bg,ed,val)-
将 之间的所有元素赋值为 。
-
复杂度为 ,常数略大于CPP
memset。在 $val=0/-1/\texttt{0x3f}$ 时常数与 `memset` 相同。(存疑) -
常用于弥补
memset无法赋值的问题,如赋值一个数组为 。
-
-
copy(bg1,ed1,bg2)- 将 中的元素复制到 。
- 复杂度 。
-
stable_sort(bg,ed)-
对 进行 稳定 排序。
-
时间复杂度 ,要 的额外空间。CPP
当空间不足时使用 $O(n\log^2n)$ 的双调排序。
-
-
nth_element(bg,md,ed):- 将 中的内容重新分配顺序,小于(等于)
md的元素在 ,大于(等于)md的元素都在 。 - 时间复杂度 ,需要 的额外空间。
- 第四个参数为比较函数,若不传则默认
less<>()。 - 常用于线性求序列中的第 大,常用于实现替罪羊树。
- 将 中的内容重新分配顺序,小于(等于)
-
max_element/min_element(bg,ed)-
返回指向 中最大 / 最小的元素的指针。
-
时间复杂度 。
-
第三个参数可传入比较函数。
-
求数组最大值就可以直接写:
*max_element(a+1,a+n+1)
-
-
random_shuffle(bg,ed)- 打乱 中元素的顺序。
- 时间复杂度 。
- 第三个参数传入一个函数
func(int n),这个函数的返回值是一个 中的随机整数。 - 在未传入第三参数时,若 那么其随机性将无法保证。
- 在
C++14中被弃用,在C++17中被废除,C++11之后都应当以shuffle替代之。
-
next_permutation(bg,ed)-
将 更改为下一个排列,并返回 。CPP
若 $[bg,ed)$ 已经是最后一个排列,那么返回 $0$。 下一个排列的定义为字典序严格大于当前排列的下一个排列。 -
时间复杂度 。
-
事实上 不一定要是排列,可以存在相同元素。
-
常用于枚举全排列:CPP
```cpp iota(p,p+n,0); do{ //do something }while(next_permutation(p,p+n)); ``` -
prev_permutation(bg,ed)可以求出上一个排列。
-
一些比较冷门的函数
-
merge(bg1,ed1,bg2,ed2,bg3)-
和 是两个有序序列,对其进行归并并存入 。CPP
**不能够原地归并**,若要原地归并请使用 `inplace_merge`。 -
时间复杂度 。
-
可以传入第六参数作为比较函数。
-
-
inplace_merge(bg,md,ed)-
将 和 归并排序,并存入 。
-
时间复杂度 ,需要 的额外空间。CPP
当空间不足时使用 $O(n\log n)$ 的原地排序。 常数较小,可能比手写的还快。 -
可以传入第四个参数作为比较函数。
-
常用于 CDQ 分治等分治算法,非常好用。
-
-
count(bg,ed,val)- 返回 中等于 的元素的个数。
- 时间复杂度 。
-
count_if(bg,ed,func)- 返回 中使得函数
func返回值为true的元素的个数。 - 时间复杂度 , 为
func的复杂度。 - 常用的
func有:islower/isupper/isdigit等。
- 返回 中使得函数
-
replace(bg,ed,val1,val2)- 将 中所有等于 的元素替换为 。
- 时间复杂度 。
-
for_each(bg,ed,func)- 对 中所有元素执行函数
func。 - 时间复杂度 。
- 其实没啥用,就是能压行。
- 对 中所有元素执行函数
-
transform(bg1,ed1,bg2,bg3,func)- 对 和 中的元素依次执行二元函数
func,并将返回值存入 。 - 时间复杂度 。
- 对 和 中的元素依次执行二元函数
-
rotate(bg,md,ed)-
将 循环至 处元素位于 。
-
时间复杂度
-
例子:CPP
```cpp vector<int>a={1,2,3,4,5}; rotate(a.begin(),a.begin()+1,a.end()); //a={2,3,4,5,1} vector<int>b={1,2,3,4,5}; rotate(b.rbegin(),b.rbegin()+1,b.rend()); //b={5,1,2,3,4} ```
-
GNU 私货
有一些以双下划线开头的函数并未在 C++ 标准中,是 GNU 的私货,在 NOI 系列赛事中可以使用。
__lg(x)- 返回 。
- 时间复杂度 。
- 常用于实现倍增、二进制运算等。
__gcd(x,y)- 返回 。
- 复杂度是对数级别的,常数较小。
- 注意,返回值的符号 不一定 是正。
- 在 C++17 之前都是很常用的。
numeric 库
这里的函数,真的很好用。
-
accumulate(bg,ed,val)-
将 中的所有所有元素与初始值 相加,返回这个和。
-
时间复杂度 。
-
可以传入第四个参数作为加法。
-
可以用于求序列和,但注意,该函数返回值与 类型一致,意味着你要注意CPP
long long的问题:```cpp accumulate(bg,ed,0);//返回值是 int,可能溢出 accumulate(bg,ed,0ll);//返回值是 long long ```
-
-
inner_product(bg1,ed1,bg2,val)- 将 和 对应位置一一相乘再与初始值 相加,返回这个和。
- 时间复杂度 。
- 可以传入第五、六个参数分别作为加法和乘法。
- 用于做向量内积。
-
partial_sum(bg1,ed1,bg2)- 对 做前缀和并存入 。
- 时间复杂度 。
- 可以传入第四个参数作为加法。
- 可以原地求前缀和。
-
adjacent_difference(bg1,ed1,bg2)- 对 求差分并存入 。
- 时间复杂度 。
- 可以传入第四个参数作为减法。
- 可以原地差分。
functional 库
常见的函数有
less<>/greater<> 等。事实上,大部分运算 / 比较也在这里:
CPPplus<>;//x+y
minus<>;//x-y
multiplies<>;//x*y
divides<>;//x/y
modulus<>;//x%y
negate<>;//-x
equal_to<>;//x==y
not_equal_to<>;//x!=y
greater<>;//x>y
less<>;//x<y
greater_equal<>;//x>=y
less_equal<>;//x<=y
logical_and<>;//x&&y
logical_or<>;//x||y
logical_not<>;//!x
bit_and<>;//x&y
bit_or<>;//x|y
bit_xor<>;//x^y
//注意 bit_not(~x) 是 C++14 的哦~
cmath 库
-
fabs(x)- 返回 。
- 注意,对浮点运算请都使用
fabs而不是abs,因为有可能你调用的是abs(int)。
-
fmod(x,y)- 返回 。
- 在一些三角函数的地方可能会用到对 取模。
-
exp(x)- 返回 。
- 在
double内, 的有效区间为 。
-
log(x)-
返回 。
-
当 时报错。
-
对数家族还有:CPP
log10(x)和log2(x)。请注意,`log2(x)` 是 C++11 的。
-
-
ceil(x)- 返回
- 其返回值依旧是浮点类型,输出时注意格式。
-
floor(x)- 返回
- 其返回值依旧是浮点类型,输出时注意格式。
__builtin 家族
这里的内容并不在 C++ 标准中,全部都是 GNU 的私货,若使用其它编译器则可能无法通过编译。
如果 的类型是
long long,请务必使用 __builtin_xxxll(x)(如 __builtin_popcountll(x)),否则将可能造成 FST 等严重后果。-
__builtin_popcount(x)-
返回 在二进制下 的个数。
-
时间复杂度有说 的,也有说 的。CPP
一定比手写的快。
-
-
__builtin_parity(x)- 返回 在二进制下 个数的奇偶性。
- 时间复杂度 ,快于
__builtin_popcount(x)&1。
-
__builtin_ffs(x)- 返回二进制下最后一个 1 是从后往前第几位。
- 时间复杂度 。
-
__builtin_ctz(x)- 返回二进制下后导零的个数, 时UB。
- 时间复杂度 。
-
__builtin_clz(x)- 返回二进制下前导零的个数, 时UB。
- 时间复杂度 。
C++ 11
从 C++98 到 C++11 是一次重大的飞跃,许许多多非常实用的函数与语法如雨后春笋般出现。
零零散散的语法糖
auto
auto 在 C++98 中是一个较偏僻冷门的东西,因此在 C++11 中直接将其抛弃,并赋予其新生。-
CPP
auto可以用来声明一个变量,其类型由初始化的内容定义。auto a=1;//a is int auto b=2.0;//b is double set<int>s; auto it=s.end();//it is set<int>::iterator因此auto声明变量时必须有初始化内容,否则将 CE。同时,也可以声明为其它变量的引用:CPPauto x=true;//x is bool auto&y=x;//y is reference of x y=false; if(x) throw;//will not run常用于声明一些类型名很长的变量,如:set<pair<int,int>,greater<pair<int,int>>>::iterator -
CPP
auto用于声明有固定返回值类型的函数,在 C++11 时需要尾随返回类,但在 C++14 及之后就不需要了:auto func(double x)->double{ return x*2-1; }//C++11 auto func2(double x){ return x*2-1; }//C++14 -
也可以用来声明一个 lambda 函数,见下文 lambda 表达式 部分。
lambda 表达式
lambda 相当于一个函数,其形式多变,但都是由若干部分组成:
CPP[captures](params)->T{body}
其中
captures 填捕获的方式,params 填传入的参数,T 填返回值类型,body 就是函数主体。-
captures有两种填法:&和=,不填时,局部的 lambda 将不会进行捕获,全局的 lambda 默认为&。CPP&表示这个表达式捕获的内容是引用的形式,而=表示捕获的内容是复制且只读的。int x=1; [=](){x=2;}();//CE,向只读变量‘x’赋值 [&](){x=2;}();//OK,x will be 2 //上面 lambda 最后的括号是对其进行调用这里有坑:请尽量使用CPP&,考虑如下程序片段:vector<int>v(n),o(n); for(int&x:v)cin>>x; iota(o.begin(),o.end(),0); sort(o.begin(),o.end(),[=](int x,int y){return v[x]<v[y];});其作用是按 的大小对其下标 排序,但注意使用的是=,这意味着每次调用 lambda 时都将 拷贝了一份,一共拷贝 次,直接 TLE。 -
params和普通函数一样。 -
T若此处省略,则其返回值将为CPPauto:[](int x){return x*2.0;}(3);//will return double 6.0
也可使用
CPPauto 将 lambda 表达式作为一个可调用的函数:auto sqr=[](double x){
return x*x;
};
那么每次调用
sqr(r) 都将返回 的值。这样写的函数将自带
inline,比写 inline 短多了。range-for
for 循环中轻松地遍历容器(vector、set、list 等):vector<int>v={5,1,3,4,2};
for(int x:v)cout<<x<<' ';
//output:5 1 3 4 2
//按顺序遍历 v 中的每一个元素,并赋值给 x。
也可以将 声明为引用:
CPPvector<int>v(n);
for(int&x:v)cin>>x;
//读入一个长度为 n 的序列
注意, 的类型必须与 中元素的类型保持一致,否则会 CE。
也可以使用
auto 进行声明。注意,若遍历数组,那将从头到尾遍历一遍,不推荐。
用处极为广泛,常用于对于
vector 存图的遍历等。STL
emplace
在很多 STL 容器中都出现了一个新的函数——
CPPemplace,如:set<int>s;
s.insert(1);//C++98
s.emplace(1);//C++11
queue<int>q;
q.push(2);//C++98
q.emplace(2);//C++11
vector<int>v;
v.push_back(3)//C++98
v.emplace_back(3);//C++11
deque<int>dq;
dq.push_front(4)//C++98
dq.emplace_front(4);//C++11
emplace 相比原先的 insert/push/push_back/push_front 区别是,emplace 通过调用元素的构造函数来进行插入,所以它会比之前的函数更快一些。因此也产生了使用上的区别:
CPPset<pair<int,int>>s;
s.insert(make_pair(1,2));//C++98
s.emplace(1,2);//C++11
更加便于书写。
但这要求用户自己的类型必须含有构造函数:
CPPstruct A{
int x;
};
queue<A>q;
q.emplace(1);//CE,A 没有构造函数
struct B{
int x;
B(int _x=0):x(_x){}
};
queue<B>p;
p.emplace(1);//OK,B 有构造函数
shrink_to_fit
vector/deque/string/basic_string 的 shrink_to_fit 可以使其 capacity 调整为 size 的大小,如:vector<int>v={1,2,3};
cout<<v.size()<<' '<<v.capacity()<<'\n';
v.clear();
cout<<v.size()<<' '<<v.capacity()<<'\n';
v.shrink_to_fit();
cout<<v.size()<<' '<<v.capacity()<<'\n';
/*
output:
3 3
0 3
0 0
*/
常用于
clear() 之后释放内存。algorithm 库
shuffle(bg,ed,gen)- 打乱 的顺序,
gen是一个随机数生成器(mt19937)。 - 时间复杂度 。
- C++11 之后请尽量使用
shuffle而不是random_shuffle
- 打乱 的顺序,
is_sorted(bg,ed)- 判断 是否升序排序。
- 时间复杂度 。
minmax(a,b)- 返回一个
pair<>,其first为 ,second为 。 - 时间复杂度 。
- 常用于无向图存边的去重问题。
- 返回一个
max(l)/min(l)- 是一个初始化列表,这个函数返回 中最大 / 最小的元素。
- 时间复杂度 。
- 在多个元素求最大 / 最小时非常好用:
max({a,b,c})
minmax(l)- 是一个初始化列表,这个函数返回一个
pair<>,其first为 中最小的元素,second为 中最大的元素。 - 时间复杂度 。
- 若要求一个序列 / 容器中最小和最大的元素,请使用
minmax_element。
- 是一个初始化列表,这个函数返回一个
minmax_element(bg,ed)- 返回一个
pair<>,其first为 中最小的元素,second为 中最大的元素。 - 时间复杂度 。
- 返回一个
numeric 库
iota(bg,ed,val)- 将 中的元素依次赋值为
- 时间复杂度 。
- 常用于给并查集初始化。
iterator 库
-
prev(it)/next(it)-
返回迭代器 的前驱 / 后继。
-
复杂度与
++/--的复杂度相同,取决于容器。 -
可以更方便的实现许多内容:CPP
```cpp set<int>s={1,2,3,4,5}; auto i=s.lower_bound(3); ++i; auto j=i; --i; //这是很麻烦的 auto j=next(i);//很方便 ``` -
请习惯在 C++98 环境下使用
next的同学们注意:这会导致声明一个叫next的变量时出错。
-
-
begin(container)/end(container)-
返回容器 的
begin()和end()。 -
复杂度取决于容器。
-
作用就是相比原先要短一个字节:CPP
```cpp set<int>t; auto i=t.begin(); auto i=begin(t);//你比一比谁更短 ```
-
functional 库
-
function声明方式:function<R(Args...)>f;其中R为返回值,Args...为形参,f为名称。一个CPPfunction可以作为函数直接使用。function<int(int,int)>f[4]={ [](int x,int y){return x+y;}, [](int x,int y){return x-y;}, [](int x,int y){return x*y;}, [](int x,int y){return x/y;} }; //声明一个数组,每个元素都是一个 function<int(int,int)> for(int i=0;i<4;++i) cout<<f[i](5,2)<<' '; //output:7 3 10 2常用来代替函数指针。
random 库
用于代替垃圾的
rand。-
mt19937-
一个类型,作为随机数生成器。
-
其构造函数应传入一个数字作为随机种子,使用时用法和
rand相同。 -
生成CPP
unsigned int范围内的随机数。```cpp mt19937 gen(123456);//123456 为随机种子 int x=gen()%10000;//x will in [0,10000) uint32_t y=gen();//normally y will in [0,2^32) ``` -
随机种子通常为时间相关,下面的非常常用,建议背过CPP
```cpp mt19937 gen(chrono::system_clock::now().time_since_epoch().count()); //chrono 在头文件 <chrono> 中 ```
-
-
uniform_int_distribution<T>(L,R)- 随机数发生器,每次调用时传入一个
mt19937,返回一个 之间的整数,类型为T,若T为空则默认为int。
- 随机数发生器,每次调用时传入一个
-
uniform_real_distribution<T>(L,R)- 随机数发生器,每次调用时传入一个
mt19937,返回一个 之间的实数,类型为T,若T为空则默认为double。
- 随机数发生器,每次调用时传入一个
mt19937 gen(chrono::system_clock::now().time_since_epoch().count());
uniform_int_distribution<>dist(1,1000);
int x=dist(gen);// x is a random int in [1,1000]
uniform_real_distribution<>dist2(-10,10);
double y=dist2(gen);// y is a random double in [-10,10]
cmath 库
exp2(x)- 返回 。
log2(x)- 返回 。
hypot(x,y)- 返回 。
- 常用于求两点之间的距离,非常方便。
NAN,INFINITY- 两个
define出的量,由编译器实现定义。
- 两个
C++14
零零散散的语法糖
- 弃用了
register。
auto
-
CPP
auto可以在 lambda 的形参中出现:void func(auto x){ //do something }但注意,在普通函数中使用auto作为形参直到 C++20 才支持。 -
CPP
auto可以作为普通函数的返回值:auto sum(int x,int y){ return x+y; }
functional 库
-
functional 库在 C++14 给大部分模板都加入了默认类型
void,如:greater<void>、plus<void>等。这里的CPPvoid可以省略,因此以后就可以不用写具体类型了:vector<pair<int,int>>v; //... sort(begin(v),end(v),greater<pair<int,int>>());//C++11 sort(begin(v),end(v),greater<>());//C++14非常方便。 -
bit_not<>在 C++98 的 functional 库中有提到。
C++17
零零散散的语法糖
结构化绑定
-
从一个结构体 / 初始化列表中获取元素.CPP
pair<int,int>a=make_pair(1,2); { int x,y; tie(x,y)=a; }//C++11 { auto[x,y]=a;//结构化绑定 }//C++17也可以声明为引用:CPPstruct A{ int u,v,w; }a; auto&[x,y,z]=a;//结构体 x=1,y=2,z=3; cout<<a.u<<' '<<a.v<<' '<<a.w<<'\n'; //output:1 2 3 -
可以搭配 range-for 使用:CPP
vector<pair<int,int>>v(n); for(auto&[x,y]:v)cin>>x>>y; -
常用于带边权的图遍历:CPP
vector<pair<int,int>>G[N]; void dfs(int u,int fa){ for(auto[v,w]:G[u])if(v!=fa) dis[v]=dis[u]+w,dfs(v,u); }非常好用。
类模板的实参推导
在使用类模板时不必写出具体的实参名:
CPPauto x=pair<int,double>(1,2.5);//C++11
auto y=pair(5,7.2);//C++17:推导了 pair<int,double>
vector<int>v={1,2,3};//C++11
vector w={1,2,3};//C++17:推导了 vector<int>
推导也可以嵌套,主要用于简化代码。
if 和 switch 中进行初始化
在 if 语句和 switch 语句中可以像 for 循环一样加入初始化语句。
CPPcin>>n;
if(int x=f(n);x>=10)
cout<<x<<'\n';
顺便提一句,在 C++98 中就已经可以这样写了:
CPPif(int x=f(n))//if x is true then:
//...
using 声明多个名称
可以使用
CPPusing 语句后跟一个逗号分隔的声明符列表。using std::cin,std::cout,std::cerr;
起到同时声明多个的作用。
对于不喜欢
using namespace std 的同学比较有用。STL
set/map 的 merge
-
将一个CPP
set/map并入另一个,若将 并入 ,则所有 中未在 里出现的元素将被插入 ,并在 中被删除。set<int>s={1,2,3}; set<int>t={3,4,5}; s.merge(t); cout<<s.size()<<" : "; for(int i:s)cout<<i<<' '; cout<<'\n'; cout<<t.size()<<" : "; for(int i:t)cout<<i<<' '; /* output: 5 : 1 2 3 4 5 1 : 3 */ -
对于
s.merge(t),复杂度为 。
algorithm 库
-
以
shuffle替代random_shuffle,random_shuffle完全被废除。 -
sample(bg,ed,out,n,gen)- 从 中随机取出 个元素,输出到
out迭代器中,随机数生成器为gen。 - 每个元素等将概率被选择。
out的一个具体的例子是back_inserter。
- 从 中随机取出 个元素,输出到
numeric 库
gcd(x,y)/lcm(x,y)- 返回 / 的值。
- 复杂度对数级别的。
- 保证了返回值的符号是正。
lcm的实现是先除后乘。
iterator 库
-
size(container)/empty(container)- 返回一个容器的
size()/ 是否为空。 - 复杂度取决于容器。
- 返回一个容器的
-
data(container)- 返回
container.data()。 - 复杂度取决于容器。
- 返回
cmath 库
- 出现了一些比较谔谔的《数学特殊函数》,OI 中应该不会用到,如果想了解请前往 官网。
hypot(x,y,z)- 返回 。
- 复杂度 。
- 常用来求三维中两点距离。
C++20
零零散散的语法糖
三路比较运算符 <=>
新增运算符
<=> 满足:- 若 ,则
(a<=>b)。 - 若 ,则
(a<=>b)。 - 若 ,则
(a<=>b)。
重载此运算符后,就可以直接使用
CPP< > <= >= 几种运算符,但请注意若要使用 == 和 != 仍需再写一个函数:struct T{
int x,y;
T(int _x=0,int _y=0):x(_x),y(_y){}
auto operator<=>(const T&_)const{
return x-_.x;
}
}a(1,3),b(2,4);
if(a<b)cout<<"a<b\n";//OK,output: a<b
if(a==b)cout<<"a==b\n";//CE not operator==
range-for 的初始化语句和初始化器
在
range-for 前面可以加入一个初始化语句 / 初始化器,可以是- 表达式语句(空语句)。
- 声明语句(或结构化绑定)。
#include<bits/stdc++.h>
using namespace std;
int main() {
vector<pair<int,int>>v={{1,2},{3,4}};
for(int i=0;auto[x,y]:v)
cout<<(i++)<<" : ("<<x<<','<<y<<")\n";
}
/*
output:
0 : (1,2)
1 : (3,4)
*/
更便利了捏~
规定有符号整数以补码实现
谔谔,谔谔。
终于可以写什么
&-2 这种东西了,好耶!STL
contains
在
set/map/multiset/multimap 中查找一个元素是否存在,如果使用 count 那么复杂度关于出现次数是线性的,这不好(出现次数为 ,则复杂度为 )。C++ 20 中加入的 contains 的复杂度是 的,返回值是 true/false ,非常好用。bit 库
C++20 加入的新库,头文件
<bit>。-
popcount(x)-
返回 无符号整形 在二进制下 的个数。
-
时间复杂度有说 的,也有说 的。一定比手写的快。
-
使用CPP
__builtin_popcount实现,但会检查传入的参数是否为无符号整形。```cpp unsigned int x=3; cout<<popcount(x)<<'\n';//OK, output:2 int y=7; cout<<popcount(y)<<'\n';//CE, y is not unsigned double z=2.5; cout<<popcount(z)<<'\n';//CE, z is not integer ``` 下述 bit 库中的函数也都会进行这个检查。
-
-
CPP
countl_zero(x);//从最高位起连续的 0 的数量 countr_zero(x);//从最低位起连续的 0 的数量 countl_one(x);//从最高位起连续的 1 的数量 countr_one(x);//从最低位起连续的 1 的数量上述函数的复杂度都是 。 -
rotl(x)/rotr(x)- 返回 二进制下逐位向左 / 右旋转得到的结果。
- 复杂度 。
- 好像没什么用?
-
bit_width(x)- 当 时返回 ,否则返回 。
- 即二进制下 的位数。
- 复杂度 。
-
bit_floor(x)/bit_ceil(x)- 返回不超过 的最大的 的整次幂 / 不小于 的最小的 的整次幂。
- 复杂度 。
-
has_single_bit(x)- 返回 是否为 的整次幂,相当于
popcount(x)==1。 - 复杂度 。
- 返回 是否为 的整次幂,相当于
ranges 库
C++20 加入的库,提供了各种处理元素范围的组件,在头文件
<ranges> ,命名空间 std::ranges::views 或 std::views 中。在 ranges 库中的一个函数
func ,其通常有两种形态:ranges::func_view 和 views::func,下文我们将全部选择更简短的后者。-
view是一个指定范围的视图,可以在 时间内完成移动 / 复制 / 赋值。这里的描述很不优美,如果看不懂描述可以直接看代码部分。 -
empty()- 一个空的
view。
- 一个空的
-
single(val)- 一个只有 的
view。
- 一个只有 的
-
iota(bg,ed)-
由 之间所有自然数组成的CPP
view。```cpp for(int i:views::iota(1,6)) cout<<i<<' '; //output:1 2 3 4 5 ``` -
若省略 则为一个无限长的序列。
-
可以搭配下文的
take一起使用。
-
-
all(v)-
包含 所有元素的CPP
view。```cpp vector v={1,2,3,4,5}; for(int i:views::all(v)) cout<<i<<' '; //output: 1 2 3 4 5 ```
-
-
transform(v,func)-
对 中每个元素执行CPP
func后的view。```cpp vector v={1,2,3,4,5}; for(int i:views::transform(v,[](int x){return x*2-1;})) cout<<i<<' '; //output:1 3 5 7 9 ```
-
-
take(v,n)-
的前 个元素组成的CPP
view。```cpp vector v={1,2,3,4,5}; for(int i:views::take(v,3)) cout<<i<<' '; //output:1 2 3 ```
-
-
take_while(v,func)-
的起始元素到首个使得CPP
func返回false为止的元素组成的view。```cpp vector v={1,2,3,4,5}; for(int i:views::take_while(v,[](int x){return x<=3;})) cout<<i<<' '; //output:1 2 3 ```
-
-
drop_while(v,func)-
跳过直到第一个使得CPP
func返回false的元素后,剩余元素组成的view。```cpp vector v={1,2,3,4,5}; for(int i:views::drop_while(v,[](int x){return x<=3;})) cout<<i<<' '; //output:4 5 ```
-
-
drop(v,n)-
的从第 个元素开始到结束(跳过前 个)的CPP
view。```cpp vector v={1,2,3,4,5}; for(int i:views::drop(v,3)) cout<<i<<' '; //output:4 5 ```
-
-
reverse(v)-
翻转 的CPP
view。```cpp vector v={1,2,3,4,5}; for(int i:views::reverse(v)) cout<<i<<' '; //output:5 4 3 2 1 ```
-
-
filter(v,func)-
使得CPP
func返回值为true的元素的view。```cpp vector v={1,2,3,4,5}; for(int i:views::filter(v,[](int x){return x&1;})) cout<<i<<' '; //output:1 3 5 ```
-
-
可以使用运算符CPP
|连接两个或多个范围适配器:vector v={1,2,3,4,5}; for(int i:v|views::reverse|views::take(3)) cout<<i<<' '; //output:5 4 3 auto odd=[](int x){return x&1;}; auto sqr=[](int x){return x*x;}; for(int i:v|views::filter(odd)|views::transform(sqr)) //output:1 9 25注意,这里的CPP|是从左向右结合的:vector v={1,2,3,4,5}; using views::take,views::drop; for(int i:v|take(3)|drop(1)) cout<<i<<' '; //output:2 3 for(int i:v|drop(1)|take(3)) cout<<i<<' '; //output:2 3 4
numbers 库
头文件
<numbers>,命名空间 std::numbers 中提供了大量的数学常数:| 代码表示 | 数学表示 |
|---|---|
e_v | |
log2e_v | |
log10e_v | |
pi_v | |
inv_pi_v | |
inv_sqrtpi_v | |
ln2_v | |
ln10_v | |
sqrt2_v | |
sqrt3_v | |
inv_sqrt3_v | |
egamma_v | 欧拉-马斯克若尼常数 |
phi_v | 黄金比 (注意这里是加号!!!) |
这些都是类模板,调用时请填入类型:
CPP#include<numbers>
#include<iomanip>
#include<iostream>
int main(){
std::cout<<std::fixed<<std::setprecision(9)<<std::numbers::pi_v<double><<'\n';
}
去掉末尾的
CPP_v 后直接就是一个常量:cout<<numbers::e<<'\n';
相关推荐
评论
共 60 条评论,欢迎与作者交流。
正在加载评论...