[Cpp基础] [04] 名字查找与函数重载

名字查找

  • 当我们使用一个名字时,编译器就会向上查找名字的声明语句.变量名的查找和函数名的查找结果有一定区别.
    • 变量名查找:最终仅会确定唯一的对象,从内到外碰到的第一个名字将被使用.
    • 函数名查找:会先确定搜索域,然后按重载规则选择最有匹配. 搜索域包含一般scope和参数scope.
  • 常规搜索域确定:从当前作用域开始,逐层向上查找名字声明,名字首次出现的作用域就是常规搜索域
  • 对于函数调用的每个实参arg_i,其类型decltype(arg_i)及其所有基类所在的作用域都加入实参类型搜索域
  • 函数名和变量名都是名字,彼此可以相互hide.

重载匹配规则

  • 函数重载的基础是传入参数的类型,和返回类型无关.对于非引用的形参,const不作为重载的依据.
  • 首先根据实参类型排除不能产生调用的函数,得到一个可行函数列表. 在可行列表中: 类型转换越少越好,在此前提下, 特化的优先于一般的.
  • 注意:
    • 仅函数/数组向指针的转换是精确匹配,只要类型不同,都被视作类型转换,例如:派生类指针到基类指针,非const对象绑定到到const T &,都是类型转换
    • 类型转换彼此没有优先级,从int 到uint并不比从int 到double要优秀.
  • 注意: 很多系统中,整数最多提升到unsigned int,这意味着以long为形参的函数很可能不会被重载系统匹配到.

using namespace XX

  • using namespace X:对当前作用域而言,相当于将名字空间X内的名字全部引入某个外层作用域.
    • using namepsace X所在语句逐层向外,当某个作用域包含X时,将名字插入这个作用域.
    • using namespace X引入的名字在外层作用域,所以不会和当前作用域的名字产生冲突,我们甚至可以在后续代码中隐藏引入的名子
    • using namespace X引入的函数名若被查找到,是在逻辑上的插入位置查找到的,而非当前作用域.因此,当前作用域定义的函数不会和using namespace引入的函数形成重载.
  • using namespace的有效区间仍仅限于所在的作用域.
  • 在类定义内不能使用using namespace
namespace Top{

namespace A{
int i=0;
}//namespace A

double i=1;

namespace B{
void fun(){
    using namespace A;//将A内的名字引入了Top作用域,此时不会产生重定义错误.
    ++i;//错误,因为使用了i而触发二义性错误
    ++Top::A::i;//正确,使用Top::A::i;
    ++Top::i;//正确,使用Top::i
    char i;//正确,隐藏之前所有的i;
}
float i=2;//定义B内的i.
}// namespace B

}// namespace Top

涉及模板时的名字查找:

  • Dependent name: 对于一个 Qualified name, 如果对于一个名字的解析依赖了非concrete的类型,那么就称之为dependent name
    • Unqualified name : 简单的标识符,不涉及::,->,.
    • Qualified name: 与上面相反,例如a.b,A::b,p_a->b,这里,b都是Qualified name.
    • 标准规定: Unqualified name 一定不是 dependent name. (这主要是为了避免写出冗杂的代码. )
  • 模板的2-phase名字查找
    • Undependent name 一定是在模板定义的位置解析及绑定的. 称之为phase 1
    • Dependent name 一定是在模板示例化的位置解析及绑定的. 称之为phase 2
// x的名字解析
// 1. int foo(){return x;}中, x 是unqualified name,进一步,一定是undependent name, 所以一定是在 phase 1 绑定的
// 2. x的查找一定不会进入Bar<T>,因为在Foo<>的定义阶段,Bar<T>仍然是个未知类型.换言之,对x的查找顺序是:foo的局部作用域-> Foo::作用域 -> 外部作用域, Bar<T>被跳过了
// 3. 注意,上面的流程是因为 x 被标准限制了, x只能是 undependent name.
// y的名字解析
// 1. 对于this->y, y是一个 qualified name ,进一步的, y的值依赖于this,而this不是concrete的,所以y是一个dependent name, 是在phase 2 绑定的
// 2. 相比于x,这里对y的查找会额外进入`Bar<T>`,因为在模板实例化阶段,`Bar<T>`已经是一个concrete的类型了.
template<class T>
struct Bar{
    int x=0;
    int y=1;
};
template<class T>
class Foo: public Bar<T>{
    int foo(){return x;} // error
    int bar(){return this->y;}// good
};

补充: dependent name的语法歧义

  • 对dependent name的解析有两种情况会出现语法歧义, 此时可能需要用户帮助编译器解决歧义.
  • 默认情况下,当B是一个dependent name时, A::B,a.B,pa->B总是被解释为一个值, 假设记作v_foo
    • A::B * a被解释为v_foo * a
    • A::B<x>a.B<x>pa->B<x>被解释为(v_foo < x) >,这将导致一个语法错误, >运算符的右侧操作数缺失了.
      • 相应的A::B<x>(y)就不会报错了,因为(v_foo < x) > (y)是一个合法的表达式
  • 当A::B为类型或者函数时, 需要通过typenametemplate帮助编译器解决歧义.
    • 例如typename A::B * a创建了一个指针,指向A::B类型
    • 例如A::template B<x>(y),产生了一个函数调用,调用了A::B<x>这个函数.

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