混合extern和const

leg*_*s2k 56 c c++ scope const extern

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

edg*_*eis 53

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

通常的模式是:

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

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

  • 嗯,既然`const`是隐含的`static`,你甚至需要``extern`来定义`a_global_var`(在*file.c*中).如果没有这个,任何包含*file.h*的东西都不会链接,因为它正在寻找带有外部链接的`const int a_global_var`. (34认同)
  • 但是,如果file.c不包含"file.h",则定义必须包含`extern` (14认同)
  • C++ 11标准的3.5/3(重点煤矿):_A名称...具有内部连接,如果它是显式声明const或constexpr和既没有明确声明为extern**也不先前声明为具有外部链接的变量的名称**; _ (10认同)
  • 除非添加显式 extern,否则其他文件无法隐式看到 const 变量。所以 file.c 应该是 `extern const int a_global_var = /* 表达式 */;` (4认同)
  • @ user123456:不,`extern`不存在是正确的。如果常量被预先声明为extern,则定义中的extern是可选的。即使没有显式extern,它也将使用外部链接定义这样的const对象。-http://stackoverflow.com/a/2151876/183120 (2认同)

Cir*_*四事件 6

C ++ 17 inline变量

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

强大的C ++ 17功能使我们能够:

main.cpp

#include <cassert>

#include "notmain.hpp"

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

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif
Run Code Online (Sandbox Code Playgroud)

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}
Run Code Online (Sandbox Code Playgroud)

编译并运行:

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
Run Code Online (Sandbox Code Playgroud)

GitHub上游

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

内联变量的C ++标准

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

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

cppreference https://zh.cppreference.com/w/cpp/language/inline解释说,如果static未给出,则具有外部链接。

内联变量实现

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

nm main.o notmain.o
Run Code Online (Sandbox Code Playgroud)

其中包含:

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
Run Code Online (Sandbox Code Playgroud)

man nmu

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

因此,我们看到有专用的ELF扩展程序。

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

extern const确实可以按照下面的示例工作,但缺点inline是:

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

main.cpp

#include <cassert>

#include "notmain.hpp"

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

notmain.cpp

#include "notmain.hpp"

const int notmain_i = 42;

const int* notmain_func() {
    return &notmain_i;
}
Run Code Online (Sandbox Code Playgroud)

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

extern const int notmain_i;

const int* notmain_func();

#endif
Run Code Online (Sandbox Code Playgroud)

GitHub上游

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

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

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

constexpr int shared_inline_constexpr() { return 42; }
Run Code Online (Sandbox Code Playgroud)

我敢打赌,任何不错的编译器都会内联该调用。

您还可以使用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;
}
Run Code Online (Sandbox Code Playgroud)

但是您不能做诸如获取其地址之类的事情,否则它会变得奇怪,请参见:https : //en.cppreference.com/w/cpp/language/static “恒定静态成员”和定义constexpr静态数据成员

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

TODO:有什么方法可以完全内联变量,而无需使用任何内存?

就像预处理器一样。

这将需要某种方式:

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

有关:

在Ubuntu 18.10,GCC 8.2.0中进行了测试。

  • “inline”有一个问题,如果另一个 DLL 已经链接到您的 DLL,并且您仅更改了该值而不是重建 DLL,则旧的 DLL 将继续使用旧值。`extern const` 解决了这个问题,但是每次链接客户端查询该值时都需要进行实际的内存查找。 (2认同)

Joh*_*ler 5

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

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