为何C++类外定义成员函数需重复标注类作用域?
问题示例
我们先看最基础的场景,定义如下Book类:
class Book { using sales = size_t; public: sales get(); };
在类外实现这个成员函数时,如果要使用类内定义的sales类型作为返回值,按照C++标准要求,必须同时给返回值类型、函数名加上类作用域运算符::,合法写法如下:
Book::sales Book::get() { }
很多人会疑惑为什么C++不支持更简洁的书写方式,比如下面这种看起来更精简的写法为什么是非法的:
Book::sales get() { }
也就是为什么C++不设计成前置的类作用域限定能覆盖整个函数定义(包含返回值和函数名),省掉函数名前重复的类名限定?
核心原因
这不是标准委员会没考虑到简化写法,而是这么设计的代价远大于省几个字符的收益:
- 首先是C++从左到右的解析硬规则。编译器解析代码时永远按从左到右的顺序做名字查找,不会提前预判后面的内容。当你写
Book::sales get()时,扫到get()的位置,编译器还没有收到任何“这是Book类成员函数”的标识,在它的视角里这就是一个返回值为Book::sales类型的全局自由函数。如果强行让前面出现的Book::作用域向后覆盖到函数名,会直接打破这套沿用了几十年的解析逻辑,催生大量无法解决的歧义——比如全局域刚好存在一个同名自由函数get,Book类里也有成员函数get,这行代码到底是定义哪个函数,编译器根本没法判定。 - 其次是类成员函数的识别逻辑。C++中只有当函数名前面带有
类名::限定时,编译器才会把这个函数识别为对应类的成员,只有完成这个识别之后,后续的函数体、尾随返回值位置才会进入类的作用域上下文,自动查找类内定义的名字。而常规的前置返回值位置在函数名之前,这时候编译器还没进入类作用域,自然无法直接识别类内定义的类型别名,必须显式加上类限定。 - 最后是历史兼容性包袱。这套解析规则从C最早的Cfront实现时期就已经确定,目前全球存在天量的存量C代码依赖这套逻辑,如果为了简化书写修改规则,会直接导致无数旧代码无法正常编译,这个成本是完全无法接受的。
如果确实想简化书写,也有合规的方案,用C++11引入的尾随返回值语法即可:这时候返回值位置在函数名之后,编译器已经识别到当前函数是Book类的成员,进入了类作用域,就不需要重复给返回值加类限定了:
auto Book::get() -> sales { }
内容的提问来源于stack exchange,提问作者air
相关产品推荐
相关产品推荐

