这篇博文是由Sunny Chatterjee和Andrew Pardoe撰写的
这个 C++核心指南 专注于提高代码正确性和安全性的简单方法。我们介绍了 C++核心指南检查器 帮助实现代码中C++核心指南的自动化。
对代码所做的最简单也是最重要的更改之一是将不可变数据标记为“const”。不是我们和核心指导方针相信:看到了吗 这篇精彩的博文 从布鲁斯道森关于增加 const
你的代码(他在MSVC上也提到了这一点 正在删除 const
可以使一些代码更快,但这反映了一个编译器错误,我们正在修复感谢布鲁斯的反馈。)因为 const
非常重要的是,我们添加了一个新的C++核心指导检查程序。 const
正确性。
我们在C++核心准则检查器中创建了四个新规则,这些规则覆盖了当前包含在 常数与不变性 C++核心指南的一节。实际上,我们并没有为所有规则添加这些检查,我们实现了对规则2的检查,“默认情况下,生成成员函数 const
“但是删除了它,因为它在有效代码上引发了太多误报。请参阅下面的详细信息。此外,该工具不会警告您可以将存根函数标记为 const
因为我们知道你可能会把 const
稍后当您实现该函数时。
常量检查器规则
Con.1:默认情况下,使对象不可变
这条规则是一个总的想法,说明我们应该始终将对象标记为 const
除非我们写信给他们。我们通过在检查器中更具体地实现后续规则来覆盖此规则。
Con.3:默认情况下,将指针和引用传递给 常数
我们的检查人员执行这个规则。您可以传递指向非常量对象的指针或引用,但如果这样做,调用者将假定其参数将被修改。如果函数没有修改对象,我们应该将对象标记为const以使意图显式。
此支票的优点
- 使被调用者的意图向调用者明确表示是否修改参数。
- 函数体中的未来修改不会改变调用者的期望。
我们使用某些启发式方法来减少噪音-
- 我们不要求将通过值传递的参数或指针参数本身标记为
const
. - 我们不要求将未使用的参数标记为
const
因为我们没有足够的信息来了解他们的意图。 - 我们不在虚拟函数上强制执行此操作,因为作者可能希望遵循特定的派生行为。
示例
// Expect 26461: The input pointer argument b in function f7 can be marked as constint f7(const int *a, int *b){ return *a + *b;}struct S0{ virtual void m();};// Expect 26461 on 'p' but don't report on unnamed parameter.S0 f8(int *p, int *){ (p == nullptr); // Don't report on return UDT. return{};}
Con.4:使用 常数 使用构造后不会更改的值定义对象的步骤
我们的检查人员执行这个规则。它类似于con.3,但适用于所有变量,而不仅仅是指针或引用参数。它有助于防止意外的对象值更改。
这种检查的优点与con.3非常相似
- 如果我们知道一个对象在声明点是否是不可变的,就更容易对代码进行推理。
- 以后对代码的修改不能更改对象的不可变属性。
像con.3一样,我们使用某些启发式方法来减少噪音-
- 我们避免在未使用的变量上使用const–它们很少添加任何值。
- 我们不建议将指针或引用本身标记为
const
,因为用户主要关心他们所指向的数据。
示例
int f5(){ // Expect 26496: Variable m is assigned only once, use const. int m = 5; const int a = 10; if (m > a) return m; return a;}
Con.5:使用 常量表达式 对于可在编译时计算的值 和 F.4:如果一个函数可能需要在编译时进行求值,那么声明它 常量表达式
我们的检查器鼓励程序员将编译时可能需要计算的函数声明为 constexpr
.
示例
// Expect 26497: could be marked constexpr if compile-time evaluation is desiredint f1(int a, int b){ return a + b;}constexpr int f2(int a, int b){ return a + b;}void f3(){ // Compile-time evaluation constexpr int m = f2(10, 20); // Expect 26498: This function call f2 can use constexpr if compile-time evaluation is desired. const int m2 = f2(10, 20);}
规则2:我们没有包括的那个
Con.2:默认情况下,生成成员函数 常数
在最初的原型中,我们包含了这个检查。但是,在对一些实际代码运行此检查之后,我们决定将其从checker的发布版本中删除。我们不希望程序员将其成员函数标记为 const
当他们在逻辑上是非- const
. 对于无畏者,在isocpp.org网页上有一个关于逻辑和物理常数的讨论: https://isocpp.org/wiki/faq/const-correctness#logical-vs物理状态 .
下面是一个示例,其中成员函数在逻辑上是非常量的。你可以标记成员函数 bar
作为 const
,例如。, void bar() const { *data_ = GetData(); }
. 但它不会改变指针 data_
它本身可以改变 data_
. 因此,这个函数在逻辑上是不正确的 const
.
class Test{public: // This method should be marked “const” since it doesn’t change the logical state of the object. MyData foo() const { return *data_; } // This method shouldn’t be blindly marked as “const”. It doesn’t alter the pointer data_ itself. // However, it alters the state of memory pointed-to by it. void bar() const { *data_ = GetData(); }private: // data_ is a pointer to a “MyData” object MyData *data_;};
将您的反馈发送给我们!
一如既往,我们欢迎您的反馈。对于问题,请通过“报告问题”选项通知我们,无论是从安装程序还是VisualStudioIDE本身 用户语音 . 您随时可以通过电子邮件联系我们cppcorecheck@microsoft.com.