我有这个代码:
// my.h
#ifndef MY_HEADER
#define MY_HEADER
int create_uid();
#endif
// my.cpp
#include "my.h"
static int _next_uid = 0;
int create_uid()
{
return _next_uid++;
}
我想内联create_uid()
,同时将_next_uid
变量保留为程序的全局变量,以便变量是唯一的。
我的问题是:
- 我可以这样做吗?
inline
语句是否要求_next_uid
在编译单元外部可见?
注意:这似乎没有清楚地回答这些问题。
澄清问题后编辑。
如果您只需要一个_next_uid
,那么只需将以下内容放入头文件中:
inline int create_uid()
{
static int _next_uid = 0;
return _next_uid++;
}
简短的回答。不。以下代码
// my.h
static int _next_uid = 0;
inline int create_uid() {
return _next_uid++;
}
可能会编译,但如果在多个翻译单元中使用,将导致未定义的行为。这是因为_next_uid
变量是不同翻译单元中的不同实体。因此,create_uid()
的定义也不同。然而:
如果具有外部链接的内联函数 [...] 在不同的翻译单元中定义不同,则行为是未定义的。[1]
相反,您可以做的是在函数中使用局部范围静态变量,就像其他答案之一 [1] 中显示的@DanielLangr。这样做的缺点是无法在函数外部访问该变量。或者,正如其中一个注释中@wohlstad提到的,您可以使用 C++17 内联变量:
// my.h
inline int _next_uid = 0;
inline int create_uid() {
return _next_uid++;
}
请注意,这不会定义静态变量。使用static
和inline
将具有与仅使用static
[3] 相同的效果,这会导致我上面提到的未定义的行为。
内联函数意味着根据定义,它使用的所有变量都必须可以从内联的翻译单元访问。这不能与唯一的静态(因此对其他 TU 不可见)变量一起使用。
[1]: https://en.cppreference.com/w/cpp/language/inline
[2]: https://stackoverflow.com/a/72124623/17862371
[3]: https://stackoverflow.com/a/58101307/17862371
总结:
如果将inline next_id()
的实现放在单个c
文件中,则不起作用,这意味着该函数位于单个编译单元中。所以main
找不到inline next_id()
,你会得到错误undefined reference
。
如果在共享头文件中声明inline next_id()
,则可以编译它,在这种情况下,每个编译单元将正确找到inline next_id()
。
就我而言,此全局变量只有一个实例将出现在进程段.DATA
虚拟地址空间中。输出编号是连续的。
例:
生成文件8:
all:
c++ -c main.cpp
c++ -c second.cpp
c++ -c share.cpp
c++ main.o second.o share.o -o main
clean:
rm -f main.o second.o share.o main
主.cpp12:
#include <cstdio>
#include "share.hpp"
#include "second.hpp"
int main(){
printf("[main] %dn", next_id());
consume_id();
printf("[main] %dn", next_id());
consume_id();
printf("[main] %dn", next_id());
return 0;
}
第二.hpp1:
void consume_id();
第二.cpp7:
#include <cstdio>
#include "share.hpp"
void consume_id(){
printf("[scnd] %dn", next_id());
}
分享.hpp4:
#pragma once
int next_id();
分享.cpp7:
static int _next_id = 0;
int next_id()
{
return _next_id++;
}
结果输出:
[main] 0
[scnd] 1
[main] 2
[scnd] 3
[main] 4
但如果改为:
分享.cpp4:
inline int next_id()
{
return _next_id++;
}
对"next_id()"的未定义引用
如果更改为
分享.hpp7:
#pragma once
static int _next_id = 0;
inline int next_id()
{
return _next_id++;
}
工程
编辑:
这似乎是一种未定义的行为
我正在使用'gcc版本11.2.0(Ubuntu 11.2.0-19ubuntu1)
就我而言
您将拥有static int _next_id
的副本,但仅在目标文件中。内存中只有一个。
objdump -d main > main.s
主143:
00000000000011b3 <_Z7next_idv>:
11b3: f3 0f 1e fa endbr64
11b7: 55 push %rbp
11b8: 48 89 e5 mov %rsp,%rbp
11bb: 8b 05 53 2e 00 00 mov 0x2e53(%rip),%eax # 4014 <_ZL8_next_id>
11c1: 8d 50 01 lea 0x1(%rax),%edx
11c4: 89 15 4a 2e 00 00 mov %edx,0x2e4a(%rip) # 4014 <_ZL8_next_id>
11ca: 5d pop %rbp
11cb: c3 ret
这里的函数_Z7next_idv
只在内存中出现 1 次。
主147:
11bb: 8b 05 53 2e 00 00 mov 0x2e53(%rip),%eax # 4014 <_ZL8_next_id>
_next_id
的标签是_ZL8_next_id
的,也只在内存中出现1次。