C++中`static const`和`const`的语义差异

174

在C++中,例如以下代码片段之间的语义区别是什么:

static const int x = 0 ;

并且

const int x = 0 ;

对于static作为链接和存储类说明符(即在函数内部和外部),都适用。


11
在C++中,“static”可能是最常被重载的关键字。根据它是在命名空间作用域、类作用域还是函数作用域,你的代码含义会有很大的差异。你可能需要澄清它的具体含义。 - sbi
3
@sbi: 我原本以为我已经做过了。函数作用域(其中它是存储类说明符)和文件作用域(其中它是链接说明符)。关于类成员和命名空间作用域变量,对于这个问题来说,我不太在意,但如果有人觉得有趣的区别,请随意提及。 - Clifford
@Clifford:非常抱歉我忽略了那些最后的话。然而,这揭示了你的一个误解:在C++中,文件作用域就是命名空间作用域。如果你在任何命名空间之外声明任何东西,它将简单地属于全局命名空间(并且可以通过前缀为“::”而没有前面的标识符来访问)。我不知道全局命名空间和其中任何嵌套命名空间之间有任何有意义的区别。对于静态对象,肯定没有任何区别。 - sbi
1
“链接”与“可见性”是不同的,如果你混淆使用它们,你会让你交流的人和自己都感到困惑。 - Ben Voigt
1
@Ben,@sbi:我并不是想表达文件作用域静态链接是一样的,只是说静态链接暗示了文件作用域。在这种意义上,作用域(或可见性)是静态和外部链接的属性,而不是它们的同义词。我认为原来的问题仍然清晰明了,我们只是在讨论对sbi有点轻蔑的评论。我们现在正在讨论英语中不太精确的语义,而不是我的理解,所以我认为我们可以停止讨论了。 - Clifford
显示剩余2条评论
2个回答

146

在C++中在文件作用域上没有区别。 const 使内部链接成为默认选项,并且所有全局变量都具有静态生存期。但是,在C中,第一个变体具有相同的行为,因此可能是使用它的好理由。

在函数内部,第二个版本可以从参数计算出来。在C或C++中,它不必像其他语言要求的那样是编译时常量。

在类内部,与函数基本相同。实例const值可以在ctor-initializer-list中计算。 static const在启动初始化期间设置,并在程序的其余部分保持不变。(注意:对于static成员的代码看起来有点不同,因为声明和初始化被分开了。)

请记住,在C++中,const表示只读,而不是常量。如果您有指向const的指针,则程序的其他部分可能会在您不注意的情况下更改该值。如果使用const定义变量,则初始化之后无法更改,但初始化仍然可以任意复杂。


2
我建议文件作用域是链接器的产物,而不是编译器,因此在语言标准中可能不会得到太多关注。严格来说,它可能是“编译单元作用域”。 - Clifford
18
“const” 表示只读,而非常量。也就是说,“编译器,如果你看到有人试图修改这个 const 的东西,请大声警告。” 这就是为什么某些东西可以同时是 const 和 volatile 的原因。 - Dan
6
如果你看到我试图修改这个常量(或者给别人修改权限),编译器会大声警告。在大多数情况下,const适用于变量的视图,而不是变量本身,其他人可以拥有相同变量的非const视图,并且当他们修改它时,编译器会保持沉默。 - Ben Voigt
1
@MichaelBurr:是的,绝对正确。我的意思是说,C++11已经移除了使用特殊情况行为的必要性,因为现在可以在整个代码中一致地使用“constexpr”。 - Ben Voigt
1
@Allanqunzi:那个句子已经很明显了。我只能补充一点,非const对象的地址会隐式转换为const T*,并将该地址存储在指向const的指针中并不会使原始对象成为const。如果尝试修改对象,则通过指向const的指针访问将生成编译器错误,但是直接使用对象名称或通过其他指针进行写入将成功。 - Ben Voigt
显示剩余8条评论

10

C++17标准草案中的const暗示在文件作用域下为static

这是来自https://dev59.com/RnA65IYBdhLWcg3wogSe#3709257的引用。

C++17 n4659标准草案 6.5 “程序和链接”:

3 具有命名空间作用域(6.3.6)的名称如果是以下之一,就具有内部链接:

  • (3.1) — 显式声明为 static 的变量、函数或函数模板;或者
  • (3.2) — 非内联的非易失性 const 限定类型的变量,既没有显式声明 extern,也没有先前声明为具有外部链接;或者
  • (3.3) — 匿名联合体的数据成员。

附录 C(供参考)兼容性,C.1.2 第 6 条:“基本概念”解释了为什么要从 C 中进行更改:

6.5 [还有 10.1.7]

更改:在 C++ 中,如果一个具有文件作用域的名称被显式声明为 const,且没有显式声明为 extern,则具有内部链接;在 C 中具有外部链接。

原因:由于在 C++ 中 const 对象可能在编译期间用作值,这个特性促使程序员为每个 const 对象提供显式初始化程序。该特性允许用户将 const 对象放置在包含在多个翻译单元中的源文件中。

对原有特性的影响:更改了规定良好的特性的语义。

转换的难度:语义转换。

使用范围:很少。

另请参阅:为什么在C ++中,const暗示内部链接,而在C中不是这样?

在头文件中可能想要做的替代方法

详细说明见:C和C ++中“const static”是什么意思?

  • 在 C++17 之前:头文件中使用 extern,在 cpp 文件中定义
  • 在 C++17 之后:在头文件中使用内联变量

谢谢,虽然我认为这与C++98相比,在C++17中并没有机会,而且这个问题是在2010年提出的。此外,你的答案只涉及静态作为链接说明符(在命名空间范围内),而问题特别询问了不同上下文中的语义。 - Clifford
@Clifford 是的,肯定比 C++17 更旧,只是懒得读所有标准;-) 我会澄清文件作用域部分。 - Ciro Santilli OurBigBook.com

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接