c++ 11
{}
大括号初始化(也称为列表初始化或统一初始化)这种初始化方式可以增加代码的清晰度,并且在一些情况下可以避免窄化转换
窄化转换是一种可能导致数据丢失或不确定行为的类型转换。例如,将一个浮点数转换为整数类型时可能发生窄化转换,浮点数的小数部分会被丢弃。
大括号列表初始化对窄化转换是比较严格的,如果初始化列表中包含了导致窄化转换的情况,编译器会发出警告或者报错。
int pivot{ 5 };
noexcept运算符
void func(int x) noexcept; //不抛出异常
void func1(int x); //抛出异常
noexcept(f()); //如果f()不抛出异常则结果为true,否则为false
noexcept(e); //当e调用的所有函数都做了不抛出说明且e本身不含有throw语句时,表达式为true,否则返回false
nullptr
避免指针类型转换问题
void foo(int a){};
void foo(char*a){};
foo(NULL);
constexpr
用于声明一个可以在编译时计算的常量表达式
constexpr int value = 42;
constexpr int* ptr = &value;
lambda
[capture list] {function body} // 自动推断返回类型,并且无参数,
[capture list] (params list) {function body} // 自动推断返回类型,并有参数
mutable
用来修饰一个 const 示例的部分可变的数据成员的,而被 mutable 修饰的数据成员,可以在 const 成员函数中修改
int x{0};
auto f2 = ([=](){x = 42;}); //会报错
auto f2 = ([=]()mutable{x = 42;});
cout << x; //输出0
完美转发
根据参数类型调用相应函数的接口,当参数是左值引用时,调用相应的函数且传递的参数是左值引用;当参数是右值引用时,调用相应的函数且传递的参数是右值引用。
template<typename T>
接收左值的函数
void f(T &){}
接收右值的函数
void f(T &&){}
万能引用
void PrintType(T&& param)
{
f(std::forward<T>(param));
}
int a = 0;
PrintType(a);//传入左值
C++ 20
<format>库
#用作前缀指示符
format({:#x} {:#d} {:#o} {:#b}", 222, 222, 222)// 0xde 222 0336 0b11011110
format({:X} {:x} {:d} {:o} {:b}", 222, 222, 222) // DE de 222 336 11011110
0用作填充字符
format("{:#08x}", 44) //0x00002c
指定输出字段的最小宽度
format("{:1}", 44) //44
format("{:*^4}", 44) //*44*
指定浮点数的精度
format("{:.1f}", 44.8190) //44.8
format("{:.1e}", 44.8190) //4.5e+01
参数值可以按照任意顺序被使用
format("The string '{0}' has {1} characters",s,s.size());
span
用于表示连续的一段内存区间,类似于一个轻量级的只读数组容器,主要用途是作为函数参数,可以避免不必要的内存拷贝,并且可以防止悬垂指针和空指针引用的问题
数组转换为 span
int arr[] = {1, 2, 3, 4, 5};
span<int> s(arr, 5);
使用 span 来遍历一个容器
vector<int> vec = {1, 2, 3, 4, 5};
for (auto&& x : span(vec)) {}
使用 span 来获取子序列
int arr[] = {1, 2, 3, 4, 5};
span<int> s(arr, 5);
auto s1 = s.subspan(1, 3);
span 转换为其他容器类型
int arr[] = {1, 2, 3, 4, 5};
span<int> s(arr, 5);
vector<int> vec(s.begin(), s.end());
模块
创建模块
export module test;
namespace test {
auto GetVer() { return "20"; }
}
引用模块
import test;
int main(){test.GetVer(); }
Coroutines协程
协程是一种特殊的函数,它的执行可以被暂停或恢复
co_wait 挂起协程, 等待其它计算完成
co_return 从协程返回 (协程 return 禁止使用)
co_yield 弹出一个值, 挂起协程, 下一次调用继续协程的运行
for co_await 循环体
generator<int> range(int start, int end) {
while (start < end) {
co_yield start;
start++;
}
// Implicit co_return at the end of this function:
// co_return;
}
for (int n : range(0, 10)) {}
Lambda 表达式的更新
C++20 开始 需要显式捕获this: [=, this]
一些函数用法
std::max_element()和std::min_element()
前两个参数分别是序列的指定范围的两个迭代器,输出为该范围中的最大/最小值的迭代器,第三个可选参数是比较器,即回调函数/函数对象
std::vector<int> numbers{ 2,3,1,4,5,7,2,2,3 };
std::max_element(std::begin(numbers), std::end(numbers));
int pivot{ 5 };
*std::max_element(std::begin(numbers), std::end(numbers), [=](int x, int y) {return std::abs(x - pivot) < std::abs(y - pivot); });
std::find()和std::find_if()
find()有三个实参,分别是序列的两个迭代器和需要寻找的元素
find_if()也有三个实参,分别是序列的两个迭代器和一个回调函数,使用回调函数可以指定搜索的方式
两者的返回值都是第一个被找到的元素的迭代器
std::vector<int> numbers{ 2,3,1,4,5,7,1,2,3 };
std::find(std::begin(numbers), std::end(numbers), 3);
std::distance(std::begin(numbers),std::find(std::begin(numbers), std::end(numbers), 3));//和首位的间隔
int divisor{ 3 };
std::find_if(std::begin(numbers), std::end(numbers), [=](int num) {return num % divisor == 0; });
std::copy()和std::copy_if()
找到满足条件的所有元素,将原来的序列中满足条件的所有元素赋值到新的序列中
copy_if前两个参数是输入迭代器,指原序列的元素范围,第三个是新的序列的迭代器,即从这个迭代器的位置开始添加筛选出来的元素,第四个参数是回调函数
copy有三个参数,和copy_if()前三个参数一样
std::vector<int> numbers{ 2,3,1,4,5,7,1,2,3 };
std::vector<int> copy (numbers.size());
auto copy_end = std::copy(std::begin(numbers), std::end(numbers),std::begin(copy));
std::vector<int> even_numbers(numbers.size());
auto copy_if_end = std::copy_if(std::begin(numbers), std::end(numbers), std::begin(even_numbers), [](int num) {return num % 2 == 0; });
even_numbers.erase(copy_if_end,even_numbers.end());//根据返回的迭代器删除后面多余的元素
std::unique和std::unique_copy() C++17
unique用于去除相邻的重复元素,所以如果要整体去重,先对数组进行个排序
std::vector<int> numbers{ 1,1,2,2,3,4,5};
std::unique(numbers.begin(), numbers.end());
unique_copy多了个copy操作,用于去除相邻的重复元素后,把元素复制到结果容器中去,不会破坏原先数组的顺序
std::vector<int> nums_new(9);
std::unique_copy(numbers.begin(), numbers.end(), nums_new.begin());
上面要先结果容器nums_new有足够的容量,否则数组越界,这时可以使用std::back_inserter()来避免预先分配容量,用多少就实际插入多少
std::vector<int> nums_new;
std::unique_copy(nums_ori.begin(), nums_ori.end(), std::back_inserter(nums_new));
remove()和remove_if()
将需要保留的元素移到容器的前面,然后把后面不需要的元素用容器的erase成员函数删掉
std::vector<int> numbers{ 2,3,1,4,5};
auto remove_end = std::remove(std::begin(numbers), std::end(numbers), 3);//返回的是需要保留元素的后一位的迭代器,减1才指向最后一个不用删的元素
numbers.erase(remove_end, std::end(numbers)); //用成员函数erase删掉后方多余的元素 2145
erase()和erase_if()
用来直接删除容器中指定的元素,这里的容器必须是支持随机访问的容器
std::vector<int> numbers{ 2,3,1,4,5};
auto erase_count = std::erase(numbers, 5); //擦除了一个5,返回值为1
auto erase_if_count = std::erase_if(numbers, [](int num) {return num % 2 == 0; });//擦除了3个偶数,返回值为3
sort()
用来排序一个元素范围,有两个重载版本,前两个参数是指定范围的首尾迭代器,第三个参数是可选的比较器(回调函数),没有指定比较器的话,默认按升序排。
vector<int> vectemp;
sort(vectemp.begin(), vectemp.end(), [](const int& a, const int& b) {return a < b; });
transform
用于将一个序列中的每个元素进行转换,并将结果存储在另一个序列中
std::string strSource;
transform(strSource.begin(), strSource.end(), strSource.begin(), tolower);
//一个数组内容复制到另一个数组中并且每个值+1
vector<int> vectemp={0,1,2,3};
vector<int> vectemp1(vectemp.size());
std::transform(vectemp.begin(), vectemp.end(), vectemp1.begin(), [](const int& a) {return a + 1; });
std::bind()
用于创建一个可调用的对象,通常称为“绑定”。这个函数允许你绑定一个或多个函数的参数,以便在稍后的时间点调用它。
void test(int x, int y) {
std::cout << x + y << "\n";
}
int main() {
auto bound_func = std::bind(test, 2, 1);
bound_func(); // 调用 bound_func()
return 0;
}
std::placeholders
想绑定部分参数时,可以使用 std::placeholders::_N 来表示占位符
auto boundFunc = std::bind(test, 10, std::placeholders::_1);
boundFunc(20); //10+20
std::for_each()
用于对容器中的所有元素执行给定的函数,在C++17中的std::for_each允许在多个线程中并发地执行操作,以提高性能。std::execution::seq:顺序执行(非并行),std::execution::par:并行执行,std::execution::par_unseq:并行和向量化执行
std::for_each(vec.begin(), vec.end(), [&](int value){ std::cout << value << std::endl; });
std::for_each(std::execution::par, numbers.begin(), numbers.end(), [](int& value) {value++;});