more c++

map

variant_constexpr

  • 多维数组如果真的用多维数组,因为有多个指针,所以更慢,而如果就用一维数组模拟存储,就不会有这种问题
  • 为什么要用begin()或者end()这个泛型,一部分是为了匹配c的老式数组

PImpl

  • 但实际上,输出流 cout 默认的设置就是“行刷新缓存”,也就是说,检测到 '\n' 时,就会自动刷新一次,根本不需要我们手动刷新!

    如果还用 endl 的话,就相当于刷新了两次,浪费性能。

back_erase

  • 通过包裹在一个lambda里面,来实现不用flag来保存状态,直接return出来

现代cmake

  • #pragma once能实现ifndef一样的事情
  • get,set只有在有必要的时候才使用,因为甚至会有性能开销
  • explicit对多个参数也有效
  • POD类型可以默认生成构造
  • 移动赋值≈解构+移动构造
  • 管理着资源:删除拷贝函数,然后统一用智能指针管理
  • 数据结构:如果可以,定义拷贝和移动
  • 非类型模板参数可以用来在编译期优化
  • 可以用一个假的模板来实现延迟编译,如果有需要才编译
  • auto作为函数返回不能分离声明与定义
  • 函数也可以 auto,lambda 也可以
  • variant:判断当前是哪个类型用 std::holds_alternative
  • 用size_t,因为会根据平台变化

SIMD

  • set内部会排序
  • 只要定义一个仿函数就能作为set的比较函数
  • 空基类,为了保证数组的不同位置指向不同的地方,必须保证类非空,即便类是空也会给他强行加一段空的大小,而如果有一个类继承了空基类,就会将空基类的这一部分空间给优化掉
  • find(x) 是找第一个等于 x 的元素。
    lower_bound(x) 找第一个大于等于 x 的元素。
    upper_bound(x) 找第一个大于 x 的元素。
  • 可以对标准库的东西定义特化

char与string

  • 为什么int等类型不用指针或者引用,是因为他们本身就比一个指针要小,一般来说一个指针8byte呢
  • span其实也是一个弱引用
  • 弱引用实际上就是用来切片的

CRTP

  • 静态或全局的是在main前初始化,main后析构的,可以通过static int a=(函数,1)来实现在一开始调用这个函数
  • 如果在同一个文件,静态变量根据其声明顺序初始化,而如果不在同一个文件中,则不一定,因此要根据链接顺序来保证他的顺序
  • 而另一种解决方式就是通过在将需要先声明的static对象弄成一个函数单例对象,而后续依赖于这个对象的变量都通过这个函数来获取这个对象,就能保证其顺序,因为函数中的static变量一定是在进入函数的时候被初始化的
  • 当对一个模板类型调用一个模板的成员的时候,需要在调用符后(::, ., →),成员名前加template空格来显示表示 显式的<>的<不是小于。

编译期

  • 由于编译期的语句是不允许未定义行为的,因此可以将函数和接受他返回值的变量设置为constexpr来检测函数中的未定义行为
  • decay能将类型的const,&,&&去掉,并将[]→*
  • declval能够根据类型凭空创造一个左值对象,但结果不能求值
  • require()和require{}其实完全不同,require{}的完整形态是require(){},但()可以省略,其整体返回一个编译期bool,可以用在各种地方,而require(编译期常量)只是用在template后面表示对其的约束

SFINAE

  • decay_t可以去掉引用,去掉cv,去掉[]等等,但不能去掉reference_wrapper
  • 可以通过逗号运算符来将多句语句组合在一起,配合…运算符展开实现for循环
  • 由于c++17中引入了if constexpr,因此一些编译期做的事情也可以写成函数式的形式,而其中有一个要解决的问题是函数式会产生实际对象,就不能用declval来解决,因此可以使用type_identity_v<>来作为declval的类似方法
  • std::source_location loc=std::source_location::current();这个函数包括调用该行的一些信息。可以用来做trace或者log,可以作为一个函数形参的默认参数来使用,因为默认参数的求值是在调用行求值的

Xmacro

random

  • inclusive_scan(begin,end,output_iterator(一般是back_inserter))就是前缀和

allocator

  • 可以使用&类型::成员名来获取其指针

类型擦除

  • 全局变量加上extern就是让编译器不把他当作一个定义,而是一个声明。而函数本身就能区别这两种,就隐式extern了。
  • 而全局的static变量或者函数就是将这个变量或者函数限制在当前翻译单元。
  • inline则是能让这个定义违反ODR原则,能够被定义多次,而在使用中,编译器会随机选择其中一个定义作为最后的定义
  • 由于模板要么显式实例化,要么就得全部公开,就很麻烦。为了解决这一问题,可以有两种办法,一种是直接将其类型擦除,另一种就是用variant来存储,而后续则使用visit进行使用。

format

  • 用户定义字面量

  • 类成员的静态初始化是无序的,因此最好还是通过函数的静态局部来让其有序
  • 当一个类要存储乱七八糟的参数的时候,就用tuple。并且,如果推导不出来,可以显式的写一个推导指引。
  • 当一个函数被声明为noexcept,他里面抛出错误的时候就会直接terminate。这样能够减少代码生成的体积,因为这样就不用管如果不声明noexcept的时候,处理异常等相关问题的代码,如析构等等。
  • 当通过…捕获异常的时候,如果想要再将他抛出,就要用std::rethrow_exception(std::current_exception());
  • 推导指引,就是在不指定<>的时候根据初始化参数来推断<>

设计模式

多线程


more c++
https://lhish.github.io/project/more c++/
作者
lhy
发布于
2025年8月16日
许可协议