每周技巧 #109:函数声明中有意义的 `const`
本节阅读量:本文翻译自 Abseil 官网的 Tip of the Week #109: Meaningful const in Function Declarations。
原文最初作为 totw/109 发布于 2016 年 1 月 14 日。
作者:Greg Miller
本文会解释什么时候 const 在函数声明中有意义,什么时候它没有意义并且最好省略。不过首先,让我们简要解释一下 declaration(声明)和 definition(定义)这两个术语的含义。
考虑下面的代码:
|
|
前两行是函数声明。函数声明告诉编译器函数的签名和返回类型。在上面的例子中,函数签名是 F(int)。函数参数类型的 const 性会被忽略,所以两个声明等价(见 “Overloadable declarations”)。
上面代码中的第 3 行和第 4 行都是函数定义。函数定义也是一种声明,但定义还包含函数体。因此,第 3 行是签名为 F(int) 的函数定义。类似地,第 4 行也是同一个函数 F(int) 的定义,这会导致链接期错误。允许多个声明,但只允许一个定义。
尽管第 3 行和第 4 行的定义声明并定义了同一个函数,但由于它们的声明方式不同,其函数体内部存在差异。在第 3 行的定义中,函数参数变量在函数内部的类型是 int(也就是非 const)。另一方面,第 4 行的定义会在函数内部产生一个类型为 const int 的函数参数变量。
函数声明中有意义的 const
并非函数声明中所有 const 限定都会被忽略。引用 C++ 标准中 “Overloadable declarations” ([over.load]) 的说法(强调为原文所加):
嵌入在参数类型说明中的
const类型说明符是有意义的,可用于区分重载函数声明。
下面是 const 有意义且不会被忽略的例子:
|
|
在上面的例子中,参数 x 本身从未被声明为 const。每个函数都接收一个名为 x、但类型不同的参数,因此形成有效的重载集。第 1 行声明了一个函数,它接收“指向 const int 的指针”。第 2 行声明了一个函数,它接收“指向 const int 的引用”。第 3 行声明了一个函数,它接收“指向 const int 的 unique_ptr”。这些 const 用法都很重要,也不会被忽略,因为它们是参数类型说明的一部分,而不是影响参数 x 本身的顶层 const 限定。
第 4 行很有意思,因为它完全不包含 const 关键字,而且根据本文开头提到的理由,乍看可能像是等价于第 1 行声明。事实并非如此,第 4 行是有效且不同的声明,原因是只有参数类型说明的顶层、最外层 const 限定会被忽略。
为了完成这个例子,我们再看几个 const 没有意义且会被忽略的例子。
|
|
经验法则
虽然我们中很少有人会真正掌握 C++ 中所有令人愉悦的晦涩之处,但尽力理解游戏规则很重要。这会帮助我们写出其他遵循相同规则、玩同一个游戏的 C++ 程序员能够理解的代码。因此,我们必须理解函数声明中什么时候 const 限定有意义,什么时候会被忽略。
尽管 Google C++ 风格指南没有给出官方指导,也没有一个普遍接受的单一意见,下面是一组合理的指导原则:
- 永远不要在非定义的函数参数声明中使用顶层
const(并注意不要复制/粘贴无意义的const)。它没有意义,会被编译器忽略,是视觉噪音,还可能误导读者。 - 在函数参数定义中是否使用顶层
const,由你(或你的团队)自行决定。你可以使用与何时把函数局部变量声明为const相同的理由。