Встроенные функции - каковы они именно в отношении ключевого слова inline?
В эта ссылка, объясняется, что такое встроенная функция и что такое ключевое слово inline. Я читаю это, потому что понял, что я никогда не понимал смысла этих двух концепций и как их следует использовать на практике. Я цитирую и комментирую ссылку, которую я предоставил
Встроенная функция или встроенная переменная (поскольку С++ 17) является функцией или переменная (начиная с С++ 17) со следующими свойствами:
1) Может быть более одного определения встроенной функции или переменной (поскольку С++ 17) в программе, если каждое определение отображается в другая единица перевода. Например, встроенная функция или встроенная переменная (поскольку С++ 17) может быть определена в файле заголовка, который include'd в нескольких исходных файлах.
Здесь у меня уже есть проблемы с пониманием, декларация - это спецификация новых идентификаторов типа
void func(void);
тогда как определение является фактической реализацией, включая тело
void func(void) {
//some code...
}
Точка 1) означает, что я могу дать различную реализацию, если они находятся в разных единицах перевода (т.е. одна реализация на заголовок e для исходных файлов), но я озадачен тем случаем, когда у меня есть исходный файл source.cc
с объявлением для func
и заголовочным файлом с другим объявлением func
единица перевода является парой source.cc+header.h
, и в таком случае, объявив два раза func
, не имеет никакого смысла, не так ли?
2) Определение встроенной функции или переменной (поскольку С++ 17) должно присутствовать в блоке перевода, к которому он доступен (не обязательно до точки доступа).
Это обычный случай, когда я отделяю определение от объявления, первое в файле заголовка, второе - в исходном файле, если мне нужно использовать функцию, я должен включить только заголовок вправо? Точка доступа будет предоставлена источником во время фазы связывания, исправьте?
3) Встроенная функция или переменная (начиная с С++ 17) с внешней связью (например, не объявленный статический) имеет следующие дополнительные свойства: 1) Он должен быть объявлен inline в каждой единицы перевода. 2) Он имеет один и тот же адрес в каждой единицы перевода.
Не могли бы вы представить простой пример того, что это значит? Я не могу представить практический пример такого случая. В случае 3) указано, что ключевое слово inline
является обязательным, если только объявляемая функция не является статической.
Насколько я понял, все правильно?
На практике функция должна быть встроенной, если такая функция очень мала, но не всегда компилятор будет встроить функцию, объявленную как встроенную, например, если она имеет петли внутри или рекурсии (например, Effective С++). В общем, тогда это зависит от компилятора, теперь я удивляюсь...
Скажем, у меня есть две функции, первая из которых является автономной (она не внутренне вызывает какую-либо другую функцию), вторая вызывает первый (вы можете предположить, что они являются как 10 строк для аргумента), Должны ли они быть объявлены inline? должны ли они быть объявлены в заголовочном файле? или я должен отделить определение в файле заголовка и реализации в исходном файле? Что было бы лучше?
Изменить 1:
Следуя одному из ответов, лучше, если я работаю по примерам, с соответствующим анализом кода сборки.
Я удалил предыдущий код, потому что это было бессмысленно (оптимизация флага -O3
не была установлена).
Я начинаю снова... У меня есть 5 файлов header.h
, src.cc
, src1.cc
, src2.cc
и main.cc
. Для каждой единицы перевода размещен соответствующий ассемблерный код.
Я манипулировал такими файлами тремя различными способами, а затем наблюдал сгенерированный код сборки, это помогло мне понять, как работает ключевое слово inline.
Пример 1:
header.h
#ifndef HEADER_H_
#define HEADER_H_
int func(int a, int b);
int test_1();
int test_2();
#endif /* HEADER_H_ */
src.cc
#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;
}
main.cc
int main(int argc, char** argv)
{
test_1();
test_2();
test_1();
test_2();
}
Сборка 1:
src.s
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
main.s
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:
header.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_ */
src.cc
#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;
}
main.cc
int main(int argc, char** argv)
{
test_1();
test_2();
test_1();
test_2();
}
Сборка 2:
src.s
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
main.s
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:
header.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_ */
src.cc
#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;
}*/
main.cc
int main(int argc, char** argv)
{
test_1();
test_2();
test_1();
test_2();
}
Сборка 3:
src.s
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
main.s
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
Может быть, несколько примеров помогут.
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; }
Каждый TU, включающий foo.h
, получает отдельную и отличную функцию 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. С++ 17 для спасения: правильные библиотеки только для заголовков
foo.h:
inline int x = 55;
inline int * f() { return &x; }
Эта версия функционально эквивалентна (1), но не требует, чтобы выделенная единица перевода содержала определения x
и f
.
Ответ 2
Точка 1) означает, что я могу дать различную реализацию, если они находятся в разных единицах перевода
Нет, он говорит, что у вас может быть несколько реализаций. Он не говорит, что они могут быть разными. Реализации должны быть одинаковыми.
Я озадачен тем, что у меня есть исходный файл source.cc
с объявлением для func
и заголовочный файл с другим объявлением func
единица перевода - это пара source.cc+header.h
и в таком случае объявив два раза func
, не имеет никакого смысла, это правильно?
Вы можете объявлять функцию столько раз, сколько захотите, сколько угодно единиц перевода, независимо от того, является ли она встроенной. Inline здесь не является фактором.
2) Определение встроенной функции или переменной (так как С++ 17) должно присутствовать в блоке перевода, к которому он обращается.
Это обычный случай, когда я отделяю определение от объявления, первое в файле заголовка, второе - в исходном файле, если мне нужно использовать функцию, я должен включить только заголовок вправо? Точка доступа будет предоставлена источником во время фазы связывания, исправьте?
Нет, определение встроенной функции должно присутствовать в каждом TU, который использует его, перед фазой связывания. Цель встроенных функций позволяет определять определения в нескольких TU; вы используете inline
, когда хотите поместить определение функции в заголовок.
В случае 3) указано, что ключевое слово inline является обязательным, если только объявляемая функция не является статической.
Нет, он вообще не говорит, что я не знаю, как вы могли бы это интерпретировать. Он просто говорит, что функция inline
static
имеет внутреннюю связь, а функция inline
non static
имеет внешнюю привязку, а подпункты 3.1 и 3.2 применяются к функциям inline
с внешней связью.
На практике функция должна быть встроенной, если такая функция очень мала, но не всегда компилятор будет встроить функцию, объявленную как встроенную, например, если она имеет петли внутри или рекурсии (например, Effective С++). В общем, тогда это зависит от компилятора, теперь я удивляюсь...
Скажем, у меня есть две функции, первая из которых является автономной (она не внутренне вызывает какую-либо другую функцию), вторая вызывает первый (вы можете предположить, что они являются как 10 строк для аргумента), Должны ли они быть объявлены inline? должны ли они быть объявлены в заголовочном файле? или я должен отделить определение в файле заголовка и реализации в исходном файле? Что было бы лучше?
Независимо от того, будет ли оптимизатор выполнять встроенную подстановку тела функции, не сильно коррелирует с тем, является ли это функцией inline
. Оптимизатор самостоятельно определит, выполнять ли встроенную подстановку функции независимо от того, является ли она функцией inline
.. Вы объявляете функции inline
, если вы хотите поместить их определение в заголовок.
Ответ 3
Задайте вопрос о том, принудительно или нет, inline
, пока что (много обсуждений по этой теме).
Вложение функции эквивалентно вставке содержимого функции в место вызова функции (вызов).
Так что дано следующее:
void Hello()
{
std::cout << "Hello\n";
}
int main()
{
Hello();
return 0;
}
Когда функция Hello
встроена, вы получите эквивалент:
int main()
{
// Hello();
std::cout << "Hello\n"; // This is the content of function Hello().
return 0;
}
Компилятору разрешено встроенные функции, которые не помечены как встроенные. Эта функция часто срабатывает при настройке оптимизации.
Изменить 1: общая причина для вложения
Общей причиной для включения функции является то, что контент меньше или равен служебной информации для вызова функции.
Существует протокол, связанный с вызовом функции, например, перемещение параметров в стек или регистры. Протокол существует независимо от размера функции. Таким образом, inlining удалит вызывающий протокол (таким образом уменьшая размер программного кода и увеличивая производительность).
Еще одна причина встроенного заключается в уменьшении количества вызовов функций. В некоторых процессорах команда перехода (вызов функции) вызывает перезагрузку кэша команд (или конвейера). Это требует времени. Inlining уменьшает вызовы функций и улучшает время выполнения.
Изменить 2: Блочный код
Одной из причин создания функций является уменьшение размера кода. Вложение больших функций может привести к раздуванию кода или увеличению размера программы.
Наложение разворотов кода и функций в рамках сделки "Время против космоса". Вложение больших функций может ускорить выполнение, но для вас это пространство. Поместить общий код в функции может уменьшить размер вашей программы, но потребуется больше времени для выполнения.