[Cpp基础] [07] 复杂类型和类型推断

数组类型和函数类型

尽管在大部分场合中,数组类型和函数类型都被转换成某种指针使用,然而它们确实是某种类型.
这一点在类型推导中很重要,因为类型推导可以推导出数组类型/函数类型

  • 数组类型逻辑上类似于std::array,由TN两个属性决定唯一的数组类型,称T[N]型对象为数组类型,其中N必须为constexpr,称T array[N]T[N]的一个实例.
    • TN必须都相同才是同一个array类型.
  • 数组类型可以被隐式decay为指向数组首元素的指针.
    • 转换为指针后,长度信息就丢失了.
    • T[N]做函数形参时,编译器一定会将形参转换为指针,从而可以接受长度不同的数组,长度信息也随之丢失.
  • 数组的长度信息未丢失时,编译器及用户是可以获取到这些长度信息的.sizeof(array)/sizeof(array[0])就是元素数量.
  • 可以使用引用型形参来保留数组的长度信息,对于模板,还可以使用自动推断.
    • 自动推断:template<class T>fun(T &);template<class T,int N>fun(T (&array)[N])
    • 手动声明:fun(int (&array)[100])
  • 由于数组是一个类型,因此尽管对于T array[N],array&array的物理地址相同,但二者的意义是不同的,前者是一个T[N]数组,可以隐式decay成一个T*,而后者则是一个指向T[N]的指针.
  • retType (arg1Type,arg2Type...)这样的类型称为函数类型.称retType fun(arg1Type,...)中的fun为该类型的实例,后文中使用using FT= retType (arg1Type,arg2Type,...)来表示函数类型.
  • 对于函数fun,fun&fun意义上是一致的,前者是一个FT型实例,会隐式decay成一个FT *,而后者就是指向FT的指针

decltype和auto

类型推断相关的内容主要有decltype和auto,其中,auto是通过初始化进行推导,其行为和模板类型推断完全一致

decltype用于获得对象的精确类型,涉及的细节较多参考Cppreference,一般来说,最好搭配std::decay使用,保证得到的类型是一个简单类型.

decltype

  • 当输入是一个对象时,推断出的类型一定和输入对象完全相同.
  • 当输入是一个左值表达式时,推断的结果是一个引用,该引用可以精确绑定到输入表达式.
  • 注意:decltype用于推断数组或函数时,不会推导出指针.对于数组decltype(array) ar;会创建一个新的数组,decltype(fun) * fp;则创建了一个函数指针.
    • 尽管decltype(fun) f2;逻辑上是创建一个"函数对象",但是由于C++中没有办法对这样创建的函数进行初始化,所以这种场景仅相当于声明函数, 也就是说: 声明了一个名为f2的函数,该函数的类型与fun一致.
  • decltype()自身就是可以作为类型使用,例如std::decay<decltype(*iter)>::type是常见的用法,它可以保证推导出一个简单类型(不含const,不含引用).

auto

auto和函数模板实参推断的行为完全一致,仅语法上形式不同,为了便于说明,本节仅用模板实参推断进行描述.

  • 当进行函数调用时,可以通过函数的实参来对模板实参进行推断,从而获得T的真实类型.
    • T && arg时,推断出的RealT一定包含引用, 且可能包含const, 会进一步按引用折叠来决定最终的类型.
    • 一般没有const T && arg这样的用法
    • T arg型形参推断的结果不含const, 推断出的数组/函数会退化为指针
    • T & arg,推断结果可以包含const,推断出的数组/函数不会退化成指针
    • const T &的推断结果肯定不含const
// 注意:直接参与推导的部分必须是`T&&`形式的才能触发引用折叠推导, 这里的`MyType<T>&& arg`并不是简单的`T&&`型, 直接参与推导的是`MyType<T>`.
// 实际的效果是: 1. 调用函数时,实参必须是MyType<T>类型的右值. 2. MyType<T> 中的T实际不需要推导,它一定和实参一致.
template<class T>
void foo2(MyType<T>&& arg){

}
  • 完美转发:将T &&std::forward配合即可, 此时推导出的T会和实参的类型完全一致, std::forward<T>(arg)的效果和cast是类似的.
template<class T>
 void Relay(T && arg){
    bar(std::forward<T>(arg));
}
  • 一般性举例
    {
    int raw;
    int any
    int &ref=raw;
    auto b=ref;// auto为int;
    auto &b1=ref;// auto为int;
    decltype(ref) c=any;// decltype(ref) 为 int& 
    }
    {
    const int raw=0;
    const int any=0;
    const int &ref=raw;
    auto b=ref;//auto为int
    auto &b1=ref;//auto为const int
    delcytple(ref) c=any;//delcytple(ref)为 const int&
    }
    int array[10]
    auto p=array;//auto为 int*
    auto &p=array;//auto为 int[10] &
    decltype(array) a2;//等价为 int a2[10];

Read more

Coroutine

从一般概念上说, 协程是特殊的函数调用: 被调用的函数可以在可控的位置被中断,然后在下一次调用时,继续从上次中断的位置继续执行。 本文主要通过Python的协程来介绍协程, 这是我唯一熟悉的一种协程实现. Classic Coroutine 下面的python代码很好的说明了协程的核心功能 def co_routine(): recv0 = yield 996 # hangs here after first coro.send assert recv0 == "Second" yield 711 # hangs here after second coro.send return def main(): coro = co_routine() # Create a new coroutine object value = coro.send(None)

By Edimetia3D

GDB with Python

这篇文章的主要应用场景是调试Python的C/C++ Extension 1. 同时使用pdb / gdb 进行调试. 通俗点说, 既可以break在 .py 文件中,也可以break在 .cc 文件中 2. 在gdb中不但可以获得常规的调试信息, 还可以获得python VM 的调试信息, 例如获得python的调用栈, 访问Python局部变量等. 这将会在调试exception时(如Segmentfalut)非常有用, 这种场景下, 定位 Python VM 正运行到哪一行代码往往可以提供一些直观的重要信息. 第一步: 编译源码以获得一些辅助数据. 我们并不真的需要使用从源码编译的Python, 但是一些调试相关的辅助文件需要从源码中获得, 包括 python-gdb.py及debug symbol等. 在 https://www.python.org/ftp/python/ 或 https://github.com/python/cpython

By Edimetia3D

Bazel Notes

这是一篇2019年左右的记录, 内容可能过时, 也不太全面 杂谈 Bazel是Google为Monorepo服务而开发的构建工具. 首先是巨大,当问题的规模变大,事情总是会变得更复杂. 而Google面对的"巨大Monorepo",应该是世间罕有的. 然后是Monorepo,这极大的影响了代码的组织风格.例如,你要写一个操作系统内核ProjectOS,还要写一个游戏ProjectGame.在传统的开发习惯中,这两个项目会组织到两个不同的Repo里,PorjectOS和ProjectGame之间无法直接相互引用,例如,你在ProjectOS里写了一个高级的数据结构,想要在Game里也使用,要么直接复制粘贴,要么是创建一个新的CommonRepo,把可公用的代码都放在Common里,然后两个项目各自引入Common作为依赖. 使用MonoRepo则不存在这个问题,Game可以直接依赖OS内的组件,按照Bazel的语法描述,就是在Game中可以直接使用@ProjectOS//path/to/package:AdvancedStruct.当然,你仍然可以选择重构一

By Edimetia3D

Unix related things

这是一篇2017年左右的记录, 仅用作分享 杂 * 在shell内能干的事,我们都可以比较简单地通过系统调用实现. * `称为反引号,^称为脱字符,常用来表示CTRL * windows的系统调用是不开放的,windows下只能直接使用windows.h里的windows API. * /dev目录下的设备是供用于程序直接使用的,主要由block,char,pipe,socket类型 * 并不是所有设备都能映射为这种形式 * /sys/device/目录称为sysfs,他下面存放了所有设备的信息.(不能直接从/dev获得任何设备信息) * udevadm info --query=all --name="/dev/sda1"可以用于查询/dev下某个设备对应的sysfs路径 权限系统 * 权限系统由两部分组成 * 文件属性:用于标注文件owner,所属组,以及权限的设定(默认只有owner和root可以修改权限设置) *

By Edimetia3D