In this article we compared different behavior of static
, inline
and static inline
free functions in compiled binary.
All the following test was done under g++ 7.1.1 on Linux amd64, ELF64.
#pragma once
inline int only_inline() { return 42; }
static int only_static() { return 42; }
static inline int static_inline() { return 42; }
#include "header.hpp"
int a()
{
return static_inline() + only_inline() + only_static();
}
#include "header.hpp"
int a();
int b()
{
return static_inline() + only_inline() + only_static();
}
int main()
{
return a() + b();
}
readelf -sW a.o | c++filt -t
# -s: symbol table
# -W: display in wide format
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.cpp
2: 0000000000000000 0 SECTION LOCAL DEFAULT 2
3: 0000000000000000 0 SECTION LOCAL DEFAULT 4
4: 0000000000000000 0 SECTION LOCAL DEFAULT 5
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
6: 0000000000000000 11 FUNC LOCAL DEFAULT 2 only_static() # local
7: 000000000000000b 11 FUNC LOCAL DEFAULT 2 static_inline() # local
8: 0000000000000000 0 SECTION LOCAL DEFAULT 8
9: 0000000000000000 0 SECTION LOCAL DEFAULT 9
10: 0000000000000000 0 SECTION LOCAL DEFAULT 7
11: 0000000000000000 0 SECTION LOCAL DEFAULT 1
12: 0000000000000000 11 FUNC WEAK DEFAULT 6 only_inline() # weak
13: 0000000000000016 37 FUNC GLOBAL DEFAULT 2 a()
Symbol table '.symtab' contains 17 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS b.cpp
2: 0000000000000000 0 SECTION LOCAL DEFAULT 2
3: 0000000000000000 0 SECTION LOCAL DEFAULT 4
4: 0000000000000000 0 SECTION LOCAL DEFAULT 5
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
6: 0000000000000000 11 FUNC LOCAL DEFAULT 2 only_static() # LOCAL
7: 000000000000000b 11 FUNC LOCAL DEFAULT 2 static_inline() # LOCAL
8: 0000000000000000 0 SECTION LOCAL DEFAULT 8
9: 0000000000000000 0 SECTION LOCAL DEFAULT 9
10: 0000000000000000 0 SECTION LOCAL DEFAULT 7
11: 0000000000000000 0 SECTION LOCAL DEFAULT 1
12: 0000000000000000 11 FUNC WEAK DEFAULT 6 only_inline() # WEAK
13: 0000000000000016 37 FUNC GLOBAL DEFAULT 2 b()
14: 000000000000003b 30 FUNC GLOBAL DEFAULT 2 main
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND a()
As in compiled executable, every copies of symbol marked LOCAL
will be preserved,
while only one instance of WEAK
symbols will only be preserved.
Symbol table '.symtab' contains 71 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000238 0 SECTION LOCAL DEFAULT 1
2: 0000000000000254 0 SECTION LOCAL DEFAULT 2
3: 0000000000000274 0 SECTION LOCAL DEFAULT 3
4: 0000000000000298 0 SECTION LOCAL DEFAULT 4
5: c0 0 SECTION LOCAL DEFAULT 5
6: 0000000000000368 0 SECTION LOCAL DEFAULT 6
7: 0000000000000412 0 SECTION LOCAL DEFAULT 7
8: 0000000000000420 0 SECTION LOCAL DEFAULT 8
9: 0000000000000440 0 SECTION LOCAL DEFAULT 9
10: 0000000000000518 0 SECTION LOCAL DEFAULT 10
11: 0000000000000530 0 SECTION LOCAL DEFAULT 11
12: 0000000000000540 0 SECTION LOCAL DEFAULT 12
13: 0000000000000550 0 SECTION LOCAL DEFAULT 13
14: 0000000000000774 0 SECTION LOCAL DEFAULT 14
15: 0000000000000780 0 SECTION LOCAL DEFAULT 15
16: 0000000000000784 0 SECTION LOCAL DEFAULT 16
17: 00000000000007f8 0 SECTION LOCAL DEFAULT 17
18: 0000000000200de0 0 SECTION LOCAL DEFAULT 18
19: 0000000000200de8 0 SECTION LOCAL DEFAULT 19
20: 0000000000200df0 0 SECTION LOCAL DEFAULT 20
21: 0000000000200fd0 0 SECTION LOCAL DEFAULT 21
22: 0000000000201000 0 SECTION LOCAL DEFAULT 22
23: 0000000000201018 0 SECTION LOCAL DEFAULT 23
24: 0000000000201028 0 SECTION LOCAL DEFAULT 24
25: 0000000000000000 0 SECTION LOCAL DEFAULT 25
26: 0000000000000000 0 FILE LOCAL DEFAULT ABS init.c
27: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
28: 0000000000000580 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
29: 00000000000005c0 0 FUNC LOCAL DEFAULT 13 register_tm_clones
30: 0000000000000610 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
31: 0000000000201028 1 OBJECT LOCAL DEFAULT 24 completed.6991
32: 0000000000200de8 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
33: 0000000000000650 0 FUNC LOCAL DEFAULT 13 frame_dummy
34: 0000000000200de0 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_
35: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.cpp
36: 000000000000065a 11 FUNC LOCAL DEFAULT 13 only_static() # COPY 1
37: 0000000000000665 11 FUNC LOCAL DEFAULT 13 static_inline() # COPY 1
38: 0000000000000000 0 FILE LOCAL DEFAULT ABS b.cpp
39: 00000000000006a0 11 FUNC LOCAL DEFAULT 13 only_static() # COPY 2
40: 00000000000006ab 11 FUNC LOCAL DEFAULT 13 static_inline() # COPY 2
41: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
42: 00000000000009e0 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
43: 0000000000000000 0 FILE LOCAL DEFAULT ABS
44: 0000000000000784 0 NOTYPE LOCAL DEFAULT 16 __GNU_EH_FRAME_HDR
45: 0000000000201000 0 OBJECT LOCAL DEFAULT 22 _GLOBAL_OFFSET_TABLE_
46: 0000000000200de8 0 NOTYPE LOCAL DEFAULT 18 __init_array_end
47: 0000000000200de0 0 NOTYPE LOCAL DEFAULT 18 __init_array_start
48: 0000000000200df0 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC
49: 0000000000201018 0 NOTYPE WEAK DEFAULT 23 data_start
50: 0000000000000770 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
51: 0000000000000550 43 FUNC GLOBAL DEFAULT 13 _start
52: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
53: 0000000000000670 37 FUNC GLOBAL DEFAULT 13 a()
54: 0000000000000774 0 FUNC GLOBAL DEFAULT 14 _fini
55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
56: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
57: 0000000000000780 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
58: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
59: 0000000000201018 0 NOTYPE GLOBAL DEFAULT 23 __data_start
60: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2
61: 0000000000201028 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__
62: 0000000000201020 0 OBJECT GLOBAL HIDDEN 23 __dso_handle
63: 0000000000000700 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init
64: 0000000000201028 0 NOTYPE GLOBAL DEFAULT 24 __bss_start
65: 0000000000000695 11 FUNC WEAK DEFAULT 13 only_inline() # only one
66: 0000000000201030 0 NOTYPE GLOBAL DEFAULT 24 _end
67: 00000000000006b6 37 FUNC GLOBAL DEFAULT 13 b()
68: 0000000000201028 0 NOTYPE GLOBAL DEFAULT 23 _edata
69: 00000000000006db 30 FUNC GLOBAL DEFAULT 13 main
70: 0000000000000518 0 FUNC GLOBAL DEFAULT 10 _init
inline
marks the symbolWEAK
, which hints linker to choose arbitary one of definition in object filesstatic
marks the symbolLOCAL
, which restricts the symbol in current translation unit, and the linker may keep multiple instances of (possibly different) definition.static inline
generally works asstatic
, but theinline
keyword suggest compiler trying to inline this function.
In C++11 it is recommended to use function in anonymous namespace over static
functions.
What if we add the following code into header.hpp
:
namespace {
int anon_namespace() { return 42; }
}
8: 0000000000000016 11 FUNC LOCAL DEFAULT 2 _ZN12_GLOBAL__N_114anon_namespaceEv # (anonymous namespace)::anon_namespace()
8: 0000000000000016 11 FUNC LOCAL DEFAULT 2 _ZN12_GLOBAL__N_114anon_namespaceEv # (anonymous namespace)::anon_namespace()
38: 0000000000000670 11 FUNC LOCAL DEFAULT 13 (anonymous namespace)::anon_namespace()
42: 00000000000006c8 11 FUNC LOCAL DEFAULT 13 (anonymous namespace)::anon_namespace()
You may notice that this is exactly the same as static
, except for its mangled name. So why the commitee introduced anonymous namespace? Read https://stackoverflow.com/questions/4977252/why-an-unnamed-namespace-is-a-superior-alternative-to-static for explanation.
- https://stackoverflow.com/questions/10876930/should-one-never-use-static-inline-function
- https://stackoverflow.com/questions/12836171/difference-between-an-inline-function-and-static-inline-function
- https://stackoverflow.com/questions/22102919/static-vs-inline-for-functions-implemented-in-header-files
Please note that the C++ Core Guidelines definitely does NOT recommend to put anonymous namespaces in header files. Referring to rule SF.21 specifically. The common convention is to use a named namespace like
detail
,internal
, or something along those lines, instead.