【问题标题】:Why is multiple definition of a const global variable allowed in C++ and not in C?为什么在 C++ 中允许对 const 全局变量进行多重定义,而在 C 中却不允许?
【发布时间】:2011-09-04 15:45:55
【问题描述】:

由于单一定义规则,在 C 或 C++ 中不允许对全局变量进行多重定义。但是,在 C++ 中,一个 const 全局变量可以在多个编译单元中定义而不会出错。这与 C 中的不同。

为什么 C++ 允许,而 C 不允许?与 C 相比,为什么 C++ 中 const 全局的用法和行为与非 const 全局不同? C++ 和 C 在 const 方面发生了什么?

例如,这在 C++ 中是允许的,但在 C 中是错误的:

// Foo.cpp
const int Foo = 99;

// Main.cpp
const int Foo = 99;
int main()
{
    cout << Foo << endl;
    return 0;
}

这对 C 来说很好,但对 C++ 来说是错误的:

// Foo.cpp
const int Foo = 99;

// Main.cpp
extern const int Foo;
int main()
{
    cout << Foo << endl;
    return 0;
}

【问题讨论】:

  • 您似乎已经回答了自己的问题...?你对哪一部分感到困惑?
  • 我猜他对这样一个事实感到困惑:他不能将 const int 声明为 extern 而不会出现链接错误。
  • Merlyn:我知道如何在 C++ 中使用 const。我很想知道为什么 const 是这样设计的,而不是像普通的全局变量?
  • 可以说,这是stackoverflow.com/questions/998425/…的副本

标签: c++ c constants one-definition-rule


【解决方案1】:
// Foo.cpp
const int Foo = 99;

// Main.cpp
const int Foo = 99;
命名空间范围内的

const 变量具有 内部 链接。所以它们基本上是两个不同的变量。没有重新定义。

来自@David 的评论,3.5/3 [basic.link]:

具有命名空间范围的名称 (3.3.5) 如果是名称,则具有内部链接
— 对象、引用、函数或 显式的函数模板 声明为静态或,
— 一个物体或 明确声明的引用 const 且均未明确声明 extern 之前也没有声明过 外部链接;或
— 数据成员 一个匿名工会的成员。


在第二种情况下,你应该这样做(正确的方法):

//Foo.h
extern const int Foo; //use extern here to make it have external linkage!

// Foo.cpp
#include "Foo.h"
const int Foo = 99; //actual definition goes here

// Main.cpp
#include "Foo.h"
int main()
{
   cout << Foo << endl;
}

【讨论】:

  • 谢谢纳瓦兹。这是否在 C++ 编程语言书或其他参考资料中的某处进行了解释?
  • 在标准中... 3.5/3 [basic.link]: 具有命名空间范围 (3.3.5) 的名称如果是对象的名称,则具有内部链接,引用, 显式声明为 static 的函数或函数模板,或者,- 显式声明为 const 且既未显式声明 extern 也未先前声明具有外部链接的对象或引用; 或 - 匿名数据成员联合。
  • @David:我在帖子中引用了您的评论。在@Ashwin 的要求下,我在标准中寻找这个文本,但你让工作变得简单。希望你不要介意。
  • 我认为Foo.h 也应该包含在Foo.cpp 中,否则Foo 不会与外部链接一起声明。
  • @Matthieu:是的。我只是忘了这样做。谢谢:-)
【解决方案2】:

我认为您要的是基本原理,而不是允许这样做的特定语言规则。

这样做的理由是它使const 变量更易于使用。它为#define 的一种常见用法提供了类型化替换。

您可以以完全相同的方式使用const int max_count = 211;,而不是#define MAX_COUNT 211,例如一个共享的头文件,而不必担心将一个定义放在哪里。

您不能合法地更改 const 对象的值,因此拥有一个对象和具有相同值的多个对象之间没有明显的区别。

由于您可以将 const 对象的定义放在头文件中,因此编译器可以轻松地在编译阶段直接使用该值,而无需将此类优化延迟到链接时修复。

【讨论】:

  • 您能否解释一下为什么 const 的这种 C++ 行为与 C 不同?上面在 C++ 中工作的代码在 C 中有多重定义错误!
  • @Ashwin:恐怕不会。我相信 C 有更多的向后兼容性问题(支持暂定定义?)并且最终得到了更妥协的const,但我没有任何直接证据证明这一点。
  • @AshwinNanjappa 我想 C 在这种情况下只是更喜欢保持关键字使用一致 - 如果您指定静态,您将获得内部链接;否则,您将获得外部链接:)
【解决方案3】:

基本上,在 C++ 中,const、非局部变量是真正的常量表达式或 constexpr。这允许很多东西,比如 TMP。

const int five = 5;
int main() {
    int x[five];
    std::array<int, five> arr;
}

在 C 中,它们只是一个不能修改的变量。也就是说,

const int five = 5;
int main() {
    int x[five]; // Technically, this is a variable length array
}

完全等价于

int five = 5;
int main() {
    int x[five];
}

实际上,C++ 将某些类型的 const 变量提升为一个新类别 constexpr,而在 C 中,这不存在,它们只是碰巧不可修改的变量。

【讨论】:

    【解决方案4】:

    【讨论】:

    • 是的,这种行为与 C 不同!你已经展示了这种混乱的另一个方面:-)
    【解决方案5】:

    为什么英国人拼写 COLOUR,而美国人拼写 COLOR?

    它们是来自相同基础的 2 种不同语言,但它们没有相同的规则。

    C 和 C++ 是相同的。如果它们没有不同,它们将被称为同一个东西。

    【讨论】:

    • “为什么它们不同?” “因为他们不一样!”没用。
    【解决方案6】:

    我的解决方法是将其声明为:

    static classfoo foo;
    

    它适用于我的情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-17
      • 1970-01-01
      • 2018-06-20
      • 1970-01-01
      • 2013-09-16
      • 1970-01-01
      • 1970-01-01
      • 2019-05-05
      相关资源
      最近更新 更多