72

我可以将 extern 和 const 混合为extern const吗?如果是,const限定符是否仅在它声明的范围内强加它的统治,还是应该完全匹配它声明的翻译单元的声明?extern const int i;即,即使实际i不是 const ,我也可以声明说,反之亦然?

4

5 回答 5

70
  • 是的,您可以一起使用它们。
  • 是的,它应该与实际声明的翻译单元中的声明完全匹配。当然,除非你参加了Underhanded C Programming Contest :-)

通常的模式是:

  • 文件.h:
    extern const int a_global_var;
  • 文件.c:
    #include "file.h"
    const int a_global_var = /* some const expression */;

编辑:合并legends2k 的评论。谢谢。

于 2010-02-03T09:42:23.567 回答
14

C++17inline变量

如果您认为自己想要一个extern const,那么您实际上更可能想要使用C++17 内联变量

这个很棒的 C++17 特性使我们能够:

主文件

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

不是main.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

非main.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

编译并运行:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub 上游.

另请参阅:内联变量如何工作?

内联变量的 C++ 标准

C++ 标准保证地址是相同的。C++17 N4659 标准草案 10.1.6“内联说明符”:

6 具有外部链接的内联函数或变量在所有翻译单元中应具有相同的地址。

cppreference https://en.cppreference.com/w/cpp/language/inline解释说如果static没有给出,那么它有外部链接。

内联变量实现

我们可以观察它是如何实现的:

nm main.o notmain.o

其中包含:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

man nmu

"u" 该符号是唯一的全局符号。这是标准 ELF 符号绑定集的 GNU 扩展。对于这样的符号,动态链接器将确保在整个过程中只有一个具有此名称和类型的符号在使用中。

所以我们看到有一个专门的 ELF 扩展。

C++ 17 之前的版本:extern const

extern const确实像下面的例子一样工作,但缺点inline是:

  • 不可能constexpr用这种技术制作变量,只inline允许:如何声明 constexpr extern?
  • 它不太优雅,因为您必须在头文件和 cpp 文件中分别声明和定义变量

主文件

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

非main.cpp

#include "notmain.hpp"

const int notmain_i = 42;

const int* notmain_func() {
    return &notmain_i;
}

不是main.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

extern const int notmain_i;

const int* notmain_func();

#endif

GitHub 上游.

仅 C++17 之前的标头替代品

这些不如extern解决方案好,但它们可以工作并且只占用一个内存位置:

一个constexpr函数,因为constexpr暗示inlineinline 允许(强制)定义出现在每个翻译单元上

constexpr int shared_inline_constexpr() { return 42; }

我敢打赌,任何体面的编译器都会内联调用。

您还可以使用一个constconstexpr静态整数变量,如:

#include <iostream>

struct MyClass {
    static constexpr int i = 42;
};

int main() {
    std::cout << MyClass::i << std::endl;
    // undefined reference to `MyClass::i'
    //std::cout << &MyClass::i << std::endl;
}

但您不能做诸如获取其地址之类的事情,否则它会被 odr-used,另请参见:https ://en.cppreference.com/w/cpp/language/static “常量静态成员”和定义 constexpr 静态数据会员

有什么方法可以完全内联吗?

TODO:有没有办法完全内联变量,而不使用任何内存?

就像预处理器所做的一样。

这将需要以某种方式:

  • 禁止或检测变量的地址是否被占用
  • 将该信息添加到 ELF 目标文件中,并让 LTO 对其进行优化

有关的:

在 Ubuntu 18.10、GCC 8.2.0 中测试。

于 2018-12-22T19:31:06.270 回答
5

你可以一起使用它们。但是您需要在使用 const 时保持一致,因为当 C++ 进行名称修饰时,const 包含在用于修饰符号名称的类型信息中。所以extern const int i将引用不同的变量extern int i

除非你使用 extern "C" {}。C名称修饰不注意const。

于 2010-02-03T09:39:14.500 回答
1

您可以一起使用它们,并且可以做各种忽略 const 关键字的事情,因为仅此而已;一个关键字。它告诉编译器你不会改变一个变量,这反过来又允许编译器做一些有用的优化,并阻止你改变你不想要的东西。

Possibility.com 有一篇不错的文章,有更多背景。

于 2010-02-03T09:38:37.823 回答
-1

是的,您可以一起使用它们。

如果您声明“extern const int i”,则 i 在其整个范围内都是 const。不可能将其重新定义为非常量。当然,您可以通过将其丢弃(使用 const_cast)来绕过 const 标志。

于 2010-02-03T09:33:32.963 回答