gpt4 book ai didi

c++ - 内联函数 - 它们与 inline 关键字相比究竟是什么?

转载 作者:IT老高 更新时间:2023-10-28 12:31:58 25 4
gpt4 key购买 nike

this link ,什么是内联函数,什么是内联关键字。我正在通读它,因为我意识到我从来没有理解这两个概念的含义以及它们应该如何在实践中使用。我从我提供的链接中引用和评论

An inline function or inline variable (since C++17) is a function or variable (since C++17) with the following properties:

1) There may be more than one definition of an inline function or variable (since C++17) in the program as long as each definition appears in a different translation unit. For example, an inline function or an inline variable (since C++17) may be defined in a header file that is include'd in multiple source files.



这里我已经有理解问题了,声明是新标识符的规范,比如
void func(void);

而定义是实际的实现,包括主体
void func(void) {
//some code...
}

第 1) 点意味着只要它们在不同的翻译单元中,我就可以给出不同的实现(即每个源文件的每个 header e 一个实现),但我很困惑我有一个源文件 source.cc带有 func 的声明和另一个声明为 func 的头文件翻译单位是对 source.cc+header.h并且在这种情况下已经声明了两次 func没有任何意义,是吗?

2) The definition of an inline function or variable (since C++17) must be present in the translation unit where it is accessed (not necessarily before the point of access).



这是我将定义与声明分开的常见情况,第一个在头文件中,第二个在源文件中,如果我需要使用该函数,我必须只包含标题,对吗?接入点将由源在链接阶段提供,对吗?

3) An inline function or variable (since C++17) with external linkage (e.g. not declared static) has the following additional properties: 1) It must be declared inline in every translation unit. 2) It has the same address in every translation unit.



你能举一个简单的例子来说明这意味着什么吗?我无法想象这样一个案例的实际案例。情况 3) 说明关键字 inline是强制性的,除非要声明的函数是静态的。

到目前为止我说的一切都正确吗?

实际上,当函数非常小时,函数应该是内联的,但编译器并不总是会内联声明为内联的函数,例如,如果它内部有循环或递归(Effective C++ 如此声明)。一般来说,它依赖于编译器,我现在很奇怪......

假设我有两个函数,第一个函数是独立的(它不会在内部调用任何其他函数),第二个函数调用的是第一个函数(为了论证,您可以假设它们都是 10 行)。他们都应该声明内联吗?它们应该在头文件中声明吗?还是应该将头文件中的定义和源文件中的实现分开?什么会更好?

编辑 1 :

如果我通过示例进行工作,并进行相关的汇编代码分析,那么遵循其中一个答案会更好。

我删除了之前的代码,因为它毫无意义(未设置 -O3 标志优化)。

我重新开始...我有 5 个文件 header.h , src.cc , src1.cc , src2.ccmain.cc .对于每个翻译单元,都发布了相关的汇编代码。

我以三种不同的方式操作了这些文件,后来观察了生成的汇编代码,这有助于我理解 inline 关键字的工作原理。

示例 1:

头文件.h
#ifndef HEADER_H_
#define HEADER_H_

int func(int a, int b);
int test_1();
int test_2();

#endif /* HEADER_H_ */

源文件
#include "header.h"

int func(int a, int b)
{
return a + b;
}

src1.cc
#include "header.h"

int test_1()
{
int a, b, c;
a = 3;
b = 7;
c = func(a, b);
return c;
}

src2.cc
#include "header.h"

int test_2()
{
int a, b, c;
a = 7;
b = 8;
c = func(a, b);
return c;
}

主文件
int main(int argc, char** argv)
{
test_1();
test_2();
test_1();
test_2();
}

组装 1:

源文件
GAS LISTING /tmp/cc0j97WY.s             page 1


1 .file "src.cc"
2 .text
3 .align 2
4 .p2align 4,,15
5 .globl _Z4funcii
6 .type _Z4funcii, @function
7 _Z4funcii:
8 .LFB2:
9 0000 8D043E leal (%rsi,%rdi), %eax
10 0003 C3 ret
11 .LFE2:
12 .size _Z4funcii, .-_Z4funcii
13 .globl __gxx_personality_v0
14 .section .eh_frame,"a",@progbits
15 .Lframe1:
16 0000 1C000000 .long .LECIE1-.LSCIE1
17 .LSCIE1:
18 0004 00000000 .long 0x0
19 0008 01 .byte 0x1
20 0009 7A505200 .string "zPR"
21 000d 01 .uleb128 0x1
22 000e 78 .sleb128 -8
23 000f 10 .byte 0x10
24 0010 06 .uleb128 0x6
25 0011 03 .byte 0x3
26 0012 00000000 .long __gxx_personality_v0
27 0016 03 .byte 0x3
28 0017 0C .byte 0xc
29 0018 07 .uleb128 0x7
30 0019 08 .uleb128 0x8
31 001a 90 .byte 0x90
32 001b 01 .uleb128 0x1
33 001c 00000000 .align 8
34 .LECIE1:
35 .LSFDE1:
36 0020 14000000 .long .LEFDE1-.LASFDE1
37 .LASFDE1:
38 0024 24000000 .long .LASFDE1-.Lframe1
39 0028 00000000 .long .LFB2
40 002c 04000000 .long .LFE2-.LFB2
41 0030 00 .uleb128 0x0
42 0031 00000000 .align 8
42 000000
43 .LEFDE1:
44 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
45 .section .note.GNU-stack,"",@progbits

src1.s
GAS LISTING /tmp/cchSilt1.s             page 1


1 .file "src1.cc"
2 .text
3 .align 2
4 .p2align 4,,15
5 .globl _Z6test_1v
6 .type _Z6test_1v, @function
7 _Z6test_1v:
8 .LFB2:
9 0000 BE070000 movl $7, %esi
9 00
10 0005 BF030000 movl $3, %edi
10 00
11 000a E9000000 jmp _Z4funcii
11 00
12 .LFE2:
13 .size _Z6test_1v, .-_Z6test_1v
14 .globl __gxx_personality_v0
15 .section .eh_frame,"a",@progbits
16 .Lframe1:
17 0000 1C000000 .long .LECIE1-.LSCIE1
18 .LSCIE1:
19 0004 00000000 .long 0x0
20 0008 01 .byte 0x1
21 0009 7A505200 .string "zPR"
22 000d 01 .uleb128 0x1
23 000e 78 .sleb128 -8
24 000f 10 .byte 0x10
25 0010 06 .uleb128 0x6
26 0011 03 .byte 0x3
27 0012 00000000 .long __gxx_personality_v0
28 0016 03 .byte 0x3
29 0017 0C .byte 0xc
30 0018 07 .uleb128 0x7
31 0019 08 .uleb128 0x8
32 001a 90 .byte 0x90
33 001b 01 .uleb128 0x1
34 001c 00000000 .align 8
35 .LECIE1:
36 .LSFDE1:
37 0020 14000000 .long .LEFDE1-.LASFDE1
38 .LASFDE1:
39 0024 24000000 .long .LASFDE1-.Lframe1
40 0028 00000000 .long .LFB2
41 002c 0F000000 .long .LFE2-.LFB2
42 0030 00 .uleb128 0x0
43 0031 00000000 .align 8
43 000000
44 .LEFDE1:
45 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
46 .section .note.GNU-stack,"",@progbits

src2.s
GAS LISTING /tmp/cc2JMtt3.s             page 1


1 .file "src2.cc"
2 .text
3 .align 2
4 .p2align 4,,15
5 .globl _Z6test_2v
6 .type _Z6test_2v, @function
7 _Z6test_2v:
8 .LFB2:
9 0000 BE080000 movl $8, %esi
9 00
10 0005 BF070000 movl $7, %edi
10 00
11 000a E9000000 jmp _Z4funcii
11 00
12 .LFE2:
13 .size _Z6test_2v, .-_Z6test_2v
14 .globl __gxx_personality_v0
15 .section .eh_frame,"a",@progbits
16 .Lframe1:
17 0000 1C000000 .long .LECIE1-.LSCIE1
18 .LSCIE1:
19 0004 00000000 .long 0x0
20 0008 01 .byte 0x1
21 0009 7A505200 .string "zPR"
22 000d 01 .uleb128 0x1
23 000e 78 .sleb128 -8
24 000f 10 .byte 0x10
25 0010 06 .uleb128 0x6
26 0011 03 .byte 0x3
27 0012 00000000 .long __gxx_personality_v0
28 0016 03 .byte 0x3
29 0017 0C .byte 0xc
30 0018 07 .uleb128 0x7
31 0019 08 .uleb128 0x8
32 001a 90 .byte 0x90
33 001b 01 .uleb128 0x1
34 001c 00000000 .align 8
35 .LECIE1:
36 .LSFDE1:
37 0020 14000000 .long .LEFDE1-.LASFDE1
38 .LASFDE1:
39 0024 24000000 .long .LASFDE1-.Lframe1
40 0028 00000000 .long .LFB2
41 002c 0F000000 .long .LFE2-.LFB2
42 0030 00 .uleb128 0x0
43 0031 00000000 .align 8
43 000000
44 .LEFDE1:
45 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
46 .section .note.GNU-stack,"",@progbits

电源
GAS LISTING /tmp/cc5CfYBW.s             page 1


1 .file "main.cc"
2 .text
3 .align 2
4 .p2align 4,,15
5 .globl main
6 .type main, @function
7 main:
8 .LFB2:
9 0000 4883EC08 subq $8, %rsp
10 .LCFI0:
11 0004 E8000000 call _Z6test_1v
11 00
12 0009 E8000000 call _Z6test_2v
12 00
13 000e E8000000 call _Z6test_1v
13 00
14 .p2align 4,,5
15 0013 E8000000 call _Z6test_2v
15 00
16 0018 31C0 xorl %eax, %eax
17 001a 4883C408 addq $8, %rsp
18 .p2align 4,,1
19 001e C3 ret
20 .LFE2:
21 .size main, .-main
22 .globl __gxx_personality_v0
23 .section .eh_frame,"a",@progbits
24 .Lframe1:
25 0000 1C000000 .long .LECIE1-.LSCIE1
26 .LSCIE1:
27 0004 00000000 .long 0x0
28 0008 01 .byte 0x1
29 0009 7A505200 .string "zPR"
30 000d 01 .uleb128 0x1
31 000e 78 .sleb128 -8
32 000f 10 .byte 0x10
33 0010 06 .uleb128 0x6
34 0011 03 .byte 0x3
35 0012 00000000 .long __gxx_personality_v0
36 0016 03 .byte 0x3
37 0017 0C .byte 0xc
38 0018 07 .uleb128 0x7
39 0019 08 .uleb128 0x8
40 001a 90 .byte 0x90
41 001b 01 .uleb128 0x1
42 001c 00000000 .align 8
43 .LECIE1:
44 .LSFDE1:
45 0020 14000000 .long .LEFDE1-.LASFDE1
46 .LASFDE1:
47 0024 24000000 .long .LASFDE1-.Lframe1
48 0028 00000000 .long .LFB2
49 002c 1F000000 .long .LFE2-.LFB2
50 0030 00 .uleb128 0x0
51 0031 44 .byte 0x4
52 .long .LCFI0-.LFB2
53 0032 0E .byte 0xe
GAS LISTING /tmp/cc5CfYBW.s page 2


54 0033 10 .uleb128 0x10
55 0034 00000000 .align 8
56 .LEFDE1:
57 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
58 .section .note.GNU-stack,"",@progbits

示例 2:

头文件.h
#ifndef HEADER_H_
#define HEADER_H_

inline int func(int a, int b)
{
return a + b;
}
int test_1();
int test_2();

#endif /* HEADER_H_ */

源文件
#include "header.h"

/*
int func(int a, int b)
{
return a + b;
}*/

src1.cc
#include "header.h"

int test_1()
{
int a, b, c;
a = 3;
b = 7;
c = func(a, b);
return c;
}

src2.cc
#include "header.h"

int test_2()
{
int a, b, c;
a = 7;
b = 8;
c = func(a, b);
return c;
}

主文件
int main(int argc, char** argv)
{
test_1();
test_2();
test_1();
test_2();
}

组装2:

源文件
GAS LISTING /tmp/cczLx8os.s             page 1


1 .file "src.cc"
2 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
3 .section .note.GNU-stack,"",@progbits

src1.s
GAS LISTING /tmp/ccMFMy9s.s             page 1


1 .file "src1.cc"
2 .text
3 .align 2
4 .p2align 4,,15
5 .globl _Z6test_1v
6 .type _Z6test_1v, @function
7 _Z6test_1v:
8 .LFB3:
9 0000 B80A0000 movl $10, %eax
9 00
10 0005 C3 ret
11 .LFE3:
12 .size _Z6test_1v, .-_Z6test_1v
13 .globl __gxx_personality_v0
14 .section .eh_frame,"a",@progbits
15 .Lframe1:
16 0000 1C000000 .long .LECIE1-.LSCIE1
17 .LSCIE1:
18 0004 00000000 .long 0x0
19 0008 01 .byte 0x1
20 0009 7A505200 .string "zPR"
21 000d 01 .uleb128 0x1
22 000e 78 .sleb128 -8
23 000f 10 .byte 0x10
24 0010 06 .uleb128 0x6
25 0011 03 .byte 0x3
26 0012 00000000 .long __gxx_personality_v0
27 0016 03 .byte 0x3
28 0017 0C .byte 0xc
29 0018 07 .uleb128 0x7
30 0019 08 .uleb128 0x8
31 001a 90 .byte 0x90
32 001b 01 .uleb128 0x1
33 001c 00000000 .align 8
34 .LECIE1:
35 .LSFDE1:
36 0020 14000000 .long .LEFDE1-.LASFDE1
37 .LASFDE1:
38 0024 24000000 .long .LASFDE1-.Lframe1
39 0028 00000000 .long .LFB3
40 002c 06000000 .long .LFE3-.LFB3
41 0030 00 .uleb128 0x0
42 0031 00000000 .align 8
42 000000
43 .LEFDE1:
44 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
45 .section .note.GNU-stack,"",@progbits

src2.s
GAS LISTING /tmp/ccNXXmLv.s             page 1


1 .file "src2.cc"
2 .text
3 .align 2
4 .p2align 4,,15
5 .globl _Z6test_2v
6 .type _Z6test_2v, @function
7 _Z6test_2v:
8 .LFB3:
9 0000 B80F0000 movl $15, %eax
9 00
10 0005 C3 ret
11 .LFE3:
12 .size _Z6test_2v, .-_Z6test_2v
13 .globl __gxx_personality_v0
14 .section .eh_frame,"a",@progbits
15 .Lframe1:
16 0000 1C000000 .long .LECIE1-.LSCIE1
17 .LSCIE1:
18 0004 00000000 .long 0x0
19 0008 01 .byte 0x1
20 0009 7A505200 .string "zPR"
21 000d 01 .uleb128 0x1
22 000e 78 .sleb128 -8
23 000f 10 .byte 0x10
24 0010 06 .uleb128 0x6
25 0011 03 .byte 0x3
26 0012 00000000 .long __gxx_personality_v0
27 0016 03 .byte 0x3
28 0017 0C .byte 0xc
29 0018 07 .uleb128 0x7
30 0019 08 .uleb128 0x8
31 001a 90 .byte 0x90
32 001b 01 .uleb128 0x1
33 001c 00000000 .align 8
34 .LECIE1:
35 .LSFDE1:
36 0020 14000000 .long .LEFDE1-.LASFDE1
37 .LASFDE1:
38 0024 24000000 .long .LASFDE1-.Lframe1
39 0028 00000000 .long .LFB3
40 002c 06000000 .long .LFE3-.LFB3
41 0030 00 .uleb128 0x0
42 0031 00000000 .align 8
42 000000
43 .LEFDE1:
44 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
45 .section .note.GNU-stack,"",@progbits

电源
GAS LISTING /tmp/cc2cc5rp.s             page 1


1 .file "main.cc"
2 .text
3 .align 2
4 .p2align 4,,15
5 .globl main
6 .type main, @function
7 main:
8 .LFB3:
9 0000 4883EC08 subq $8, %rsp
10 .LCFI0:
11 0004 E8000000 call _Z6test_1v
11 00
12 0009 E8000000 call _Z6test_2v
12 00
13 000e E8000000 call _Z6test_1v
13 00
14 .p2align 4,,5
15 0013 E8000000 call _Z6test_2v
15 00
16 0018 31C0 xorl %eax, %eax
17 001a 4883C408 addq $8, %rsp
18 .p2align 4,,1
19 001e C3 ret
20 .LFE3:
21 .size main, .-main
22 .globl __gxx_personality_v0
23 .section .eh_frame,"a",@progbits
24 .Lframe1:
25 0000 1C000000 .long .LECIE1-.LSCIE1
26 .LSCIE1:
27 0004 00000000 .long 0x0
28 0008 01 .byte 0x1
29 0009 7A505200 .string "zPR"
30 000d 01 .uleb128 0x1
31 000e 78 .sleb128 -8
32 000f 10 .byte 0x10
33 0010 06 .uleb128 0x6
34 0011 03 .byte 0x3
35 0012 00000000 .long __gxx_personality_v0
36 0016 03 .byte 0x3
37 0017 0C .byte 0xc
38 0018 07 .uleb128 0x7
39 0019 08 .uleb128 0x8
40 001a 90 .byte 0x90
41 001b 01 .uleb128 0x1
42 001c 00000000 .align 8
43 .LECIE1:
44 .LSFDE1:
45 0020 14000000 .long .LEFDE1-.LASFDE1
46 .LASFDE1:
47 0024 24000000 .long .LASFDE1-.Lframe1
48 0028 00000000 .long .LFB3
49 002c 1F000000 .long .LFE3-.LFB3
50 0030 00 .uleb128 0x0
51 0031 44 .byte 0x4
52 .long .LCFI0-.LFB3
53 0032 0E .byte 0xe
GAS LISTING /tmp/cc2cc5rp.s page 2


54 0033 10 .uleb128 0x10
55 0034 00000000 .align 8
56 .LEFDE1:
57 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
58 .section .note.GNU-stack,"",@progbits

示例 3:

头文件.h
#ifndef HEADER_H_
#define HEADER_H_

inline int func(int a, int b)
{
return a + b;
}
inline int test_1()
{
int a, b, c;
a = 3;
b = 7;
c = func(a, b);
return c;
}
inline int test_2()
{
int a, b, c;
a = 7;
b = 8;
c = func(a, b);
return c;
}

#endif /* HEADER_H_ */

源文件
#include "header.h"

/*
int func(int a, int b)
{
return a + b;
}*/

src1.cc
#include "header.h"

/*int test_1()
{
int a, b, c;
a = 3;
b = 7;
c = func(a, b);
return c;
}*/

src2.cc
#include "header.h"


/*int test_2()
{
int a, b, c;
a = 7;
b = 8;
c = func(a, b);
return c;
}*/

主文件
int main(int argc, char** argv)
{
test_1();
test_2();
test_1();
test_2();
}

装配 3:

源文件
GAS LISTING /tmp/ccfPkzMC.s             page 1


1 .file "src.cc"
2 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
3 .section .note.GNU-stack,"",@progbits

src1.s
GAS LISTING /tmp/cckRkoWG.s             page 1


1 .file "src1.cc"
2 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
3 .section .note.GNU-stack,"",@progbits

src2.s
GAS LISTING /tmp/ccfmb3gI.s             page 1


1 .file "src2.cc"
2 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
3 .section .note.GNU-stack,"",@progbits

电源
GAS LISTING /tmp/ccGBsR8z.s             page 1


1 .file "main.cc"
2 .text
3 .align 2
4 .p2align 4,,15
5 .globl main
6 .type main, @function
7 main:
8 .LFB5:
9 0000 31C0 xorl %eax, %eax
10 0002 C3 ret
11 .LFE5:
12 .size main, .-main
13 .globl __gxx_personality_v0
14 .section .eh_frame,"a",@progbits
15 .Lframe1:
16 0000 1C000000 .long .LECIE1-.LSCIE1
17 .LSCIE1:
18 0004 00000000 .long 0x0
19 0008 01 .byte 0x1
20 0009 7A505200 .string "zPR"
21 000d 01 .uleb128 0x1
22 000e 78 .sleb128 -8
23 000f 10 .byte 0x10
24 0010 06 .uleb128 0x6
25 0011 03 .byte 0x3
26 0012 00000000 .long __gxx_personality_v0
27 0016 03 .byte 0x3
28 0017 0C .byte 0xc
29 0018 07 .uleb128 0x7
30 0019 08 .uleb128 0x8
31 001a 90 .byte 0x90
32 001b 01 .uleb128 0x1
33 001c 00000000 .align 8
34 .LECIE1:
35 .LSFDE1:
36 0020 14000000 .long .LEFDE1-.LASFDE1
37 .LASFDE1:
38 0024 24000000 .long .LASFDE1-.Lframe1
39 0028 00000000 .long .LFB5
40 002c 03000000 .long .LFE5-.LFB5
41 0030 00 .uleb128 0x0
42 0031 00000000 .align 8
42 000000
43 .LEFDE1:
44 .ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
45 .section .note.GNU-stack,"",@progbits

示例 1 和示例 3 是我特别感兴趣的那些,因为它们应该以某种方式突出显示内联函数和非内联函数之间的区别(按照我上面发布的链接的第 1,2 和 3 点),与内联版本相比,我没有看到非内联函数缺少任何属性。有人可以为我强调不同之处吗(再次就第 1,2 和 3 点而言)?

最佳答案

也许举几个例子会有所帮助。

1. 传统编译库

foo.h:

extern int x;
int * f();

foo.cpp:
#include "foo.h"

int x = 25;

int * f() { return &x; }

用户包括 foo.h并且需要在包含 foo.cpp 的翻译单元中进行链接如需调用 f .每个这样的调用都返回相同的地址。

2. 单独的 TU 局部变量

foo.h:
static int x = 35;
static int * f() { return &x; }

每个包含 foo.h 的 TU得到一个单独的和不同的函数 f , 调用这会导致每个 TU 的唯一值。

3.宝贝第一次ODR违规

foo.h:
static int x = 45;
inline int * f() { return &x; }

这似乎是一个只有头文件的库,但如果 foo.h包含在多个 TU 中,这构成 ODR 违规,因为 f将被定义多次,但并非所有定义都相同。

这是一个常见的错误。解决方法包括制作 x模板或替换 x具有类似 int & x() { static int impl = 45; return impl; } 的功能.请注意,如果您省略了 static ,由于 x 的多个定义,您很可能会收到链接器错误。 ; static看似“使代码编译”。

4. C++17 来拯救:正确的头文件库

foo.h:
inline int x = 55;
inline int * f() { return &x; }

此版本在功能上等同于 (1),但不需要专门的翻译单元来包含 x 的定义。和 f .

关于c++ - 内联函数 - 它们与 inline 关键字相比究竟是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41491221/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com