このリンク では、インライン関数とは何か、インラインキーワードとは何かが説明されています。これらの2つの概念の意味と、実際にどのように使用すべきかを理解したことがないことに気付いたので、読んでいます。私が提供したリンクから引用してコメントしています
インライン関数またはインライン変数(C++ 17以降)は、次のプロパティを持つ関数または変数(C++ 17以降)です。
1)各定義が異なる変換単位で表示される限り、プログラムにはインライン関数または変数の定義が複数存在する場合があります(C++ 17以降)。たとえば、インライン関数またはインライン変数(C++ 17以降)は、複数のソースファイルにインクルードされているヘッダーファイルで定義できます。
ここで私はすでに問題を理解しています、宣言は次のような新しい識別子の仕様です
_void func(void);
_
定義は本体を含む実際の実装ですが
_void func(void) {
//some code...
}
_
ポイント1)は、異なる変換単位(つまり、ソースファイルごとにヘッダーごとに1つの実装)にある限り、異なる実装を与えることができることを意味しますが、ソースファイルがある場合は困惑します_source.cc
_func
の宣言とfunc
の別の宣言を持つヘッダーファイルはペア_source.cc+header.h
_であり、そのような場合、func
を2回宣言しても意味がありません。
2)インライン関数または変数の定義(C++ 17以降)は、アクセスされる変換ユニットに存在する必要があります(必ずしもアクセスポイントの前にある必要はありません)。
これは、定義を宣言から分離する通常のケースです。最初はヘッダーファイルにあり、2番目はソースファイルにあります。関数を使用する必要がある場合は、ヘッダーのみをインクルードする必要がありますか?アクセスポイントは、リンクフェーズ中にソースによって提供されますよね?
3)外部リンケージ(静的宣言されていないなど)を持つインライン関数または変数(C++ 17以降)には、次の追加プロパティがあります。1)すべての変換ユニットでインライン宣言する必要があります。 2)すべての翻訳単位で同じアドレスを持っています。
これが何を意味するのか簡単な例を教えてください。そのような場合の実際的な事例を想像することはできません。ケース3)は、宣言される関数が静的でない限り、キーワードinline
が必須であると述べています。
私がこれまでに言ったことはすべて正しいですか?
実際には、そのような関数が非常に小さい場合、関数はインラインである必要がありますが、たとえば内部にループがある場合や再帰がある場合など、コンパイラがインラインとして宣言された関数をインライン化するとは限りません(有効なC++はそう述べています)。一般的に、それはコンパイラに依存します、私は今疑問に思います...
2つの関数があるとします。最初の関数は自己完結型であり(内部で他の関数を呼び出すことはありません)、2番目の関数は最初の関数です(引数のために両方とも10行であると想定できます)。それらの両方がインラインで宣言する必要がありますか?それらはヘッダーファイルで宣言する必要がありますか?または、ヘッダーファイルの定義とソースファイルの実装を分離する必要がありますか?何が良いでしょうか?
編集1:
関連するアセンブリコード分析を使用して例を使用する場合は、答えの1つに従う方が適切です。
前のコードは無意味だったので削除しました(_-O3
_フラグの最適化が設定されていませんでした)。
もう一度始めます... 5つのファイル_header.h
_、_src.cc
_、_src1.cc
_、_src2.cc
_、および_main.cc
_があります。各翻訳ユニットについて、関連するアセンブリコードが掲載されています。
このようなファイルを3つの異なる方法で操作し、後で生成されたアセンブリコードを観察しました。これは、インラインキーワードがどのように機能するかを理解するのに役立ちました。
例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に関して)?
たぶん、いくつかの例が役立つでしょう。
foo.h:
_extern int x;
int * f();
_
foo.cpp:
_#include "foo.h"
int x = 25;
int * f() { return &x; }
_
ユーザーには_foo.h
_が含まれており、f
を呼び出すには、_foo.cpp
_を含む翻訳単位にリンクする必要があります。そのような呼び出しはすべて同じアドレスを返します。
foo.h:
_static int x = 35;
static int * f() { return &x; }
_
_foo.h
_を含むすべてのTUは、separateおよびdistinct関数を取得しますf
、TUごとに一意の値になる呼び出し。
foo.h:
_static int x = 45;
inline int * f() { return &x; }
_
これはヘッダーのみのライブラリのようですが、_foo.h
_が複数のTUに含まれている場合、f
は複数回定義されますが、すべての定義ではないため、これはODR違反になります。同一になります。
これはよくある間違いです。回避策には、x
をテンプレートにする、またはx
をint & x() { static int impl = 45; return impl; }
などの関数に置き換えるなどがあります。 static
を省略すると、x
の定義が複数あるため、リンカーエラーが発生する可能性が高いことに注意してください。 static
は「コードをコンパイルする」ようです。
foo.h:
_inline int x = 55;
inline int * f() { return &x; }
_
このバージョンは機能的に(1)と同等ですが、x
およびf
の定義を含む専用の変換ユニットは必要ありません。
ポイント1)は、異なる翻訳単位にある限り、異なる実装を提供できることを意味します
いいえ、複数の実装が可能であると書かれています。それらが異なる可能性があるとは言っていません。実装はすべて同一である必要があります。
func
の宣言を含むソースファイル_source.cc
_と、func
の別の宣言を含むヘッダーファイルがある場合、私は困惑します。翻訳単位はペア_source.cc+header.h
_そしてそのような場合に2回func
を宣言しても意味がありません、そうですか?
関数は、インラインであるかどうかに関係なく、必要な数の変換単位で何度でも宣言できます。ここではインラインは要因ではありません。
2)インライン関数または変数(C++ 17以降)の定義は、アクセスされる変換ユニットに存在する必要があります。
これは、定義を宣言から分離する通常のケースです。最初はヘッダーファイルにあり、2番目はソースファイルにあります。関数を使用する必要がある場合は、ヘッダーのみをインクルードする必要がありますか?アクセスポイントは、リンクフェーズ中にソースによって提供されますよね?
いいえ、インライン関数のdefinitionは、リンクフェーズの前に、それを使用するすべてのTU)に存在する必要があります。 allow定義に対するインライン関数の目的です複数のTUで、wantを使用して関数の定義をヘッダーに配置する場合は、inline
を使用します。
ケース3)は、宣言される関数が静的でない限り、キーワードインラインが必須であると述べています。
いいえ、それはまったく言っていません、あなたがそれをそのように解釈することができた方法がわかりません。 inline
static
関数には内部リンケージがあり、inline
非static
関数には外部リンケージがあり、サブポイント3.1と3.2が適用されるとだけ言っています。 inline
は外部リンケージで機能します。
実際には、そのような関数が非常に小さい場合、関数はインラインである必要がありますが、たとえば内部にループがある場合や再帰がある場合など、コンパイラがインラインとして宣言された関数をインライン化するとは限りません(有効なC++はそう述べています)。一般的に、それはコンパイラに依存します、私は今疑問に思います...
2つの関数があるとします。最初の関数は自己完結型であり(内部で他の関数を呼び出すことはありません)、2番目の関数は最初の関数です(引数のために両方とも10行であると想定できます)。それらの両方がインラインで宣言する必要がありますか?それらはヘッダーファイルで宣言する必要がありますか?または、ヘッダーファイルの定義とソースファイルの実装を分離する必要がありますか?何が良いでしょうか?
オプティマイザーが関数本体のインライン置換を実行するかどうかは、それがinline
関数であるかどうかとは強く相関していません。 オプティマイザは、関数がinline
関数であるかどうかに関係なく、関数のインライン置換を実行するかどうかを自動的に判断します。関数inline
を宣言します。それらの定義をヘッダーに入れたい。
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:インライン化の一般的な理由
関数をインライン化する一般的な理由は、コンテンツが関数を呼び出すオーバーヘッドよりも小さいか等しい場合です。
パラメータをスタックまたはレジスタに移動するなど、関数の呼び出しに関連するプロトコルがあります。プロトコルは、関数のサイズに関係なく存在します。したがって、インライン化すると呼び出し元のプロトコルが削除されます(したがって、プログラムコードのサイズが小さくなり、パフォーマンスが向上します)。
インライン化するもう1つの理由は、関数呼び出しの量を減らすことです。一部のプロセッサでは、分岐命令(関数呼び出し)により、命令キャッシュ(またはパイプライン)が再ロードされます。これには時間がかかります。インライン化により、関数呼び出しが削減され、実行時間が改善されます。
編集2:コード膨張
関数を作成する理由の1つは、コードサイズを小さくすることです。大きな関数をインライン化すると、コードの膨張またはプログラムのサイズが大きくなる可能性があります。
コードの膨張と関数のインライン化は、時間と空間のトレードオフの下にあります。大きな関数のインライン化は実行をスピードアップするかもしれませんが、あなたはそれとスペースを交換しています。共通のコードを関数に配置すると、プログラムのサイズが小さくなる可能性がありますが、実行に時間がかかります。