内联函数——它们与内联关键字对应的是什么?

时间:2020-12-31 18:33:59

In this link, what is an inline function and what is the inline keyword is explained. I'm reading through it because I realized I've never understood the meaning of these two concepts and how they should be used in practice. I'm quoting and commenting from the link I provided

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

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

内联函数或内联变量(因为c++ 17)是一个函数或变量(因为c++ 17)具有以下属性:

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.

在程序中,只要每个定义出现在不同的翻译单元中,就可能有不止一个内联函数或变量的定义(因为c++ 17)。例如,可以在包含在多个源文件中的头文件中定义内联函数或内联变量(因为c++ 17)。

Here I already have understanding problems, declaration is the specification of new identifiers like

这里我已经理解了一些问题,声明是新的标识符的规范,比如

void func(void);

while a definition is the actual implementation, including the body

定义是实际的实现,包括主体

void func(void) {
  //some code...
}

The point 1) means that I can give different implementation as long as they're in different translation units (i.e. one implementation per header e per source files), but I'm puzzled in the case I have a source file source.cc with a declaration for func and an header file with another declaration of func the translation unit is the pair source.cc+header.h and in such a case having declared two times func doesn't make any sense, is that right?

点1)意味着我可以提供不同的实现,只要它们在不同的转换单元中(例如,每个源文件每个头e有一个实现),但是对于我有一个源文件源的情况,我感到很困惑。cc带有func的声明和带有func的另一个声明的头文件,翻译单元是一对source.cc+头。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).

2)内联函数或变量的定义(因为c++ 17)必须出现在被访问的翻译单元中(不一定是在访问点之前)。

This is the usual case where I separate definition from declaration, the first in an header file, the second one is in the source file, if I need to use the function I have to include only the header right? The access point would be provided by the source during the linking phase, correct?

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

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)具有外部链接的内联函数或变量(因为c++ 17)具有以下附加属性:1)它必须在每个翻译单元中声明为内联。每个翻译单位都有相同的地址。

Could you provide a simple example of what this means? I can't picture a practical case of such a case. The case 3) states that the keyword inline is mandatory unless the function to be declared is static.

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

Is everything I said so far correct?

到目前为止我说的都对吗?

In practice a function should be inline when such a function is very small, but not always the compiler would inline the function declared as inline, for example if it has loops inside or recursion (Effective C++ states so). In general then it's compiler dependent, I the wonder now...

在实践中,当这样的函数非常小的时候,函数应该是内联的,但并不总是编译器会内联声明为内联的函数,例如,如果它内部有循环或递归(有效的c++状态)。一般来说,它是依赖于编译器的,我现在很好奇……

Say I have two functions the first one is self-contained (it doesn't internally call any-other function), the second one call's the first one (you can assume they're both 10 lines for sake of argument). Should both of them declared inline? should they be declared in an header file? or should I separate definition in an header file and the implementation in an source file? What would be better?

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

Edit 1:

编辑1:

Following one of the answer is better if I work by examples, with related assembly code analysis.

如果我使用示例(使用相关的汇编代码分析),那么遵循其中一个答案会更好。

I removed the previous code because it was meaningless (the -O3 flag optimization wasn't set).

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

I start again... I have 5 files header.h,src.cc, src1.cc, src2.cc and main.cc. For each translation unit the related assembly code is posted.

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

I've manipulated such files in three different ways and later observed the assembly code generated, this helped me to understand how the inline keyword works.

我以三种不同的方式处理这些文件,然后观察生成的汇编代码,这帮助我理解内联关键字是如何工作的。

Example 1:

示例1:

header.h

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

src.cc

#include "header.h"

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

src1.cc

src1.cc

#include "header.h"

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

src2.cc

src2.cc

#include "header.h"

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

main.cc

main.cc

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

Assembly 1:

组装1:

src.s

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

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

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

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

Example 2:

示例2:

header.h

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

src.cc

#include "header.h"

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

src1.cc

src1.cc

#include "header.h"

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

src2.cc

src2.cc

#include "header.h"

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

main.cc

main.cc

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

Assembly 2:

组装2:

src.s

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

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

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

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

Example 3:

示例3:

header.h

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

src.cc

#include "header.h"

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

src1.cc

src1.cc

#include "header.h"

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

src2.cc

src2.cc

#include "header.h"


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

main.cc

main.cc

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

Assembly 3:

组装3:

src.s

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

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

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

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

Example 1 and example 3 are the ones I'm particularly interested in, because they should highlight somehow what is the difference between an inline function and a not inline function (following the points 1,2 and 3 of the link I posted above), I don't see any lack of properties in the not inline functions compared to the inline version. Can someone highlight the difference for me (again in terms of the points 1,2 and 3)?

例1和例3的我特别感兴趣,因为他们应该突出某种程度上的区别是什么一个内联函数和非内联函数(后点1、2和3的链接我上面贴),我没有看到任何缺乏属性不内联函数内联版本相比。能不能给我强调一下不同点?

3 个解决方案

#1


42  

Maybe a few examples would help.

也许几个例子会有帮助。

1. Traditional compiled library

foo.h:

foo。:

extern int x;
int * f();

foo.cpp:

foo.cpp:

#include "foo.h"

int x = 25;

int * f() { return &x; }

Users include foo.h and need to link in the translation unit containing foo.cpp in order to call f. Every such call returns the same address.

用户包括foo。并且需要在包含foo的翻译单元中链接。为了调用f, cpp会返回相同的地址。

2. Separate, TU-local variables

foo.h:

foo。:

static int x = 35;
static int * f() { return &x; }

Every TU that includes foo.h gets a separate and distinct function f, calling which results in a unique value per TU.

所有包含foo的TU。h得到一个单独的、不同的函数f,调用它会得到每TU的唯一值。

3. Baby's first ODR violation

foo.h:

foo。:

static int x = 45;
inline int * f() { return &x; }

This appears to be a header-only library, but if foo.h is included in more than one TU, this constitutes an ODR violation, since f would be defined more than once but not all of its definitions would be identical.

这似乎是一个只用于头部的库,但是如果是foo。h包含在多个TU中,这就构成了ODR违背,因为f将被定义多次,但不是所有的定义都是相同的。

This is a common mistake. Workarounds include things like making x a template or replacing x with a function like int & x() { static int impl = 45; return impl; }. Note that if you omit the static, you would most likely get a linker error because of multiple definitions of x; static seemingly "makes the code compile".

这是一个常见的错误。解决方法包括将x作为模板,或用int & x() {static int impl = 45;返回impl;}。注意,如果省略了静态,很可能会由于x的多个定义而导致链接器错误;静态看起来“使代码编译”。

4. C++17 to the rescue: Proper header-only libraries

foo.h:

foo。:

inline int x = 55;
inline int * f() { return &x; }

This version is functionally equivalent to (1), but does not require a dedicated translation unit to contain the definitions of x and f.

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

#2


15  

The point 1) means that I can give different implementation as long as they're in different translation units

重点1)意味着只要它们在不同的翻译单元中,我就可以提供不同的实现

No, it says you can have more than one implementation. It does not say they can be different. The implementations must all be identical.

不,它说您可以有多个实现。它并没有说它们可以不同。实现必须完全相同。

I'm puzzled in the case I have a source file source.cc with a declaration for func and an header file with another declaration of func the translation unit is the pair source.cc+header.h and in such a case having declared two times func doesn't make any sense, is that right?

我对我有一个源文件源感到困惑。cc带有func的声明和带有func的另一个声明的头文件,翻译单元是一对source.cc+头。h,在这种情况下声明两次func是没有意义的,对吗?

You can declare a function as many times as you like, in as many translation units as you like, regardless of whether or not it is inline. Inline is not a factor here.

无论函数是否内联,都可以在任意多的翻译单元中任意声明一次。内联在这里不是一个因素。

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

2)内联函数或变量的定义(因为c++ 17)必须出现在被访问的翻译单元中。

This is the usual case where I separate definition from declaration, the first in an header file, the second one is in the source file, if I need to use the function I have to include only the header right? The access point would be provided by the source during the linking phase, correct?

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

No, the definition of an inline function must be present in every TU that uses it, before the linking phase. It is the purpose of inline functions to allow definitions in multiple TUs; you use inline when you want to put the definition of a function in a header.

不,在链接阶段之前,内联函数的定义必须出现在使用它的每个TU中。内联函数的目的是允许在多个TUs中定义;当您想要将函数的定义放到header中时,您可以使用inline。

The case 3) states that the keyword inline is mandatory unless the function to be declared is static.

案例3)声明关键字inline是强制性的,除非要声明的函数是静态的。

No, it doesn't say that at all, I don't know how you could have interpreted it that way. It just says that an inline static function has internal linkage, and an inline non-static function has external linkage, and subpoints 3.1 and 3.2 apply to inline functions with external linkage.

不,它根本没说,我不知道你怎么能这样解释。它只是说内联静态函数有内部链接,内联非静态函数有外部链接,子点3.1和3.2适用于具有外部链接的内联函数。

In practice a function should be inline when such a function is very small, but not always the compiler would inline the function declared as inline, for example if it has loops inside or recursion (Effective C++ states so). In general then it's compiler dependent, I the wonder now...

在实践中,当这样的函数非常小的时候,函数应该是内联的,但并不总是编译器会内联声明为内联的函数,例如,如果它内部有循环或递归(有效的c++状态)。一般来说,它是依赖于编译器的,我现在很好奇……

Say I have two functions the first one is self-contained (it doesn't internally call any-other function), the second one call's the first one (you can assume they're both 10 lines for sake of argument). Should both of them declared inline? should they be declared in an header file? or should I separate definition in an header file and the implementation in an source file? What would be better?

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

Whether or not the optimizer will perform inline substitution of a function body is not strongly correlated with whether it is an inline function. The optimizer will figure out for itself whether to perform inline substitution of a function, regardless of whether or not it is an inline function. You declare functions inline if you want to put their definition in a header.

优化器是否会执行一个函数体的内联替换与它是否是内联函数并没有很强的关联。优化器将自行决定是否对函数执行内联替换,而不管它是否是内联函数。如果要将函数的定义放在标题中,可以声明为内联的函数。

#3


8  

Let's put the issue of whether inline is forced or not, aside for now (there are lot's of discussions on the topic).

让我们暂时把内联是否强制的问题放在一边(关于这个主题有很多讨论)。

Inlining a function is equivalent of pasting the contents of the function at the location of the function call (invocation).

内联函数相当于将函数的内容粘贴到函数调用(调用)的位置。

So given the following:

鉴于以下几点:

void Hello()
{
  std::cout << "Hello\n";
}

int main()
{
  Hello();
  return 0;
}

When the Hello function is inlined, you will get the equivalent of:

当Hello函数内联时,您将得到等价的:

int main()
{
  // Hello();
  std::cout << "Hello\n"; // This is the content of function Hello().
  return 0;
}

The compiler is allowed to inline functions that are not marked as being inlined. This feature is often triggered by an optimization setting.

编译器允许内联函数,这些函数不被标记为内联。此特性通常由优化设置触发。

Edit 1: Common reason for inlining
A common reason for inlining a function is when the content is smaller or equal than the overhead to call the function.

编辑1:内联函数的常见原因是内容小于或等于调用函数的开销。

There is a protocol associated with call a function, such as moving parameters to the stack or registers. The protocol exists regardless of the size of the function. So, inlining will remove the calling protocol (thus reducing program code size and increasing performance).

有一个与调用函数相关的协议,例如将参数移动到堆栈或寄存器。无论函数的大小,协议都存在。因此,内联将删除调用协议(从而减少程序代码大小并提高性能)。

Another reason to inline is to reduce the quantity of function calls. In some processors, a branch instruction (function call), causes the instruction cache (or pipeline) to be reloaded. This takes time. Inlining reduces the function calls and improves execution time.

内联的另一个原因是减少函数调用的数量。在某些处理器中,一个分支指令(函数调用)导致指令缓存(或管道)被重新加载。这需要时间。内联减少了函数调用,提高了执行时间。

Edit 2: Code Bloat
One reason to create functions is to reduce code size. Inlining of large functions may result in code bloat or the increasing of the program's size.

编辑2:代码膨胀创建函数的一个原因是减少代码大小。内联大型函数可能导致代码膨胀或程序的大小增加。

Code bloat and function inlining are under the Time versus Space trade off. Inlining of large functions may speed up execution, but you are trading space for it. Place common code into functions may decrease the size of your program, but take more time to execute.

代码膨胀和功能内联是在时间和空间权衡下进行的。大型函数的内联可能会加速执行,但你是在为它交易空间。将公共代码放置到函数中可以减少程序的大小,但是需要更多的时间来执行。

#1


42  

Maybe a few examples would help.

也许几个例子会有帮助。

1. Traditional compiled library

foo.h:

foo。:

extern int x;
int * f();

foo.cpp:

foo.cpp:

#include "foo.h"

int x = 25;

int * f() { return &x; }

Users include foo.h and need to link in the translation unit containing foo.cpp in order to call f. Every such call returns the same address.

用户包括foo。并且需要在包含foo的翻译单元中链接。为了调用f, cpp会返回相同的地址。

2. Separate, TU-local variables

foo.h:

foo。:

static int x = 35;
static int * f() { return &x; }

Every TU that includes foo.h gets a separate and distinct function f, calling which results in a unique value per TU.

所有包含foo的TU。h得到一个单独的、不同的函数f,调用它会得到每TU的唯一值。

3. Baby's first ODR violation

foo.h:

foo。:

static int x = 45;
inline int * f() { return &x; }

This appears to be a header-only library, but if foo.h is included in more than one TU, this constitutes an ODR violation, since f would be defined more than once but not all of its definitions would be identical.

这似乎是一个只用于头部的库,但是如果是foo。h包含在多个TU中,这就构成了ODR违背,因为f将被定义多次,但不是所有的定义都是相同的。

This is a common mistake. Workarounds include things like making x a template or replacing x with a function like int & x() { static int impl = 45; return impl; }. Note that if you omit the static, you would most likely get a linker error because of multiple definitions of x; static seemingly "makes the code compile".

这是一个常见的错误。解决方法包括将x作为模板,或用int & x() {static int impl = 45;返回impl;}。注意,如果省略了静态,很可能会由于x的多个定义而导致链接器错误;静态看起来“使代码编译”。

4. C++17 to the rescue: Proper header-only libraries

foo.h:

foo。:

inline int x = 55;
inline int * f() { return &x; }

This version is functionally equivalent to (1), but does not require a dedicated translation unit to contain the definitions of x and f.

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

#2


15  

The point 1) means that I can give different implementation as long as they're in different translation units

重点1)意味着只要它们在不同的翻译单元中,我就可以提供不同的实现

No, it says you can have more than one implementation. It does not say they can be different. The implementations must all be identical.

不,它说您可以有多个实现。它并没有说它们可以不同。实现必须完全相同。

I'm puzzled in the case I have a source file source.cc with a declaration for func and an header file with another declaration of func the translation unit is the pair source.cc+header.h and in such a case having declared two times func doesn't make any sense, is that right?

我对我有一个源文件源感到困惑。cc带有func的声明和带有func的另一个声明的头文件,翻译单元是一对source.cc+头。h,在这种情况下声明两次func是没有意义的,对吗?

You can declare a function as many times as you like, in as many translation units as you like, regardless of whether or not it is inline. Inline is not a factor here.

无论函数是否内联,都可以在任意多的翻译单元中任意声明一次。内联在这里不是一个因素。

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

2)内联函数或变量的定义(因为c++ 17)必须出现在被访问的翻译单元中。

This is the usual case where I separate definition from declaration, the first in an header file, the second one is in the source file, if I need to use the function I have to include only the header right? The access point would be provided by the source during the linking phase, correct?

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

No, the definition of an inline function must be present in every TU that uses it, before the linking phase. It is the purpose of inline functions to allow definitions in multiple TUs; you use inline when you want to put the definition of a function in a header.

不,在链接阶段之前,内联函数的定义必须出现在使用它的每个TU中。内联函数的目的是允许在多个TUs中定义;当您想要将函数的定义放到header中时,您可以使用inline。

The case 3) states that the keyword inline is mandatory unless the function to be declared is static.

案例3)声明关键字inline是强制性的,除非要声明的函数是静态的。

No, it doesn't say that at all, I don't know how you could have interpreted it that way. It just says that an inline static function has internal linkage, and an inline non-static function has external linkage, and subpoints 3.1 and 3.2 apply to inline functions with external linkage.

不,它根本没说,我不知道你怎么能这样解释。它只是说内联静态函数有内部链接,内联非静态函数有外部链接,子点3.1和3.2适用于具有外部链接的内联函数。

In practice a function should be inline when such a function is very small, but not always the compiler would inline the function declared as inline, for example if it has loops inside or recursion (Effective C++ states so). In general then it's compiler dependent, I the wonder now...

在实践中,当这样的函数非常小的时候,函数应该是内联的,但并不总是编译器会内联声明为内联的函数,例如,如果它内部有循环或递归(有效的c++状态)。一般来说,它是依赖于编译器的,我现在很好奇……

Say I have two functions the first one is self-contained (it doesn't internally call any-other function), the second one call's the first one (you can assume they're both 10 lines for sake of argument). Should both of them declared inline? should they be declared in an header file? or should I separate definition in an header file and the implementation in an source file? What would be better?

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

Whether or not the optimizer will perform inline substitution of a function body is not strongly correlated with whether it is an inline function. The optimizer will figure out for itself whether to perform inline substitution of a function, regardless of whether or not it is an inline function. You declare functions inline if you want to put their definition in a header.

优化器是否会执行一个函数体的内联替换与它是否是内联函数并没有很强的关联。优化器将自行决定是否对函数执行内联替换,而不管它是否是内联函数。如果要将函数的定义放在标题中,可以声明为内联的函数。

#3


8  

Let's put the issue of whether inline is forced or not, aside for now (there are lot's of discussions on the topic).

让我们暂时把内联是否强制的问题放在一边(关于这个主题有很多讨论)。

Inlining a function is equivalent of pasting the contents of the function at the location of the function call (invocation).

内联函数相当于将函数的内容粘贴到函数调用(调用)的位置。

So given the following:

鉴于以下几点:

void Hello()
{
  std::cout << "Hello\n";
}

int main()
{
  Hello();
  return 0;
}

When the Hello function is inlined, you will get the equivalent of:

当Hello函数内联时,您将得到等价的:

int main()
{
  // Hello();
  std::cout << "Hello\n"; // This is the content of function Hello().
  return 0;
}

The compiler is allowed to inline functions that are not marked as being inlined. This feature is often triggered by an optimization setting.

编译器允许内联函数,这些函数不被标记为内联。此特性通常由优化设置触发。

Edit 1: Common reason for inlining
A common reason for inlining a function is when the content is smaller or equal than the overhead to call the function.

编辑1:内联函数的常见原因是内容小于或等于调用函数的开销。

There is a protocol associated with call a function, such as moving parameters to the stack or registers. The protocol exists regardless of the size of the function. So, inlining will remove the calling protocol (thus reducing program code size and increasing performance).

有一个与调用函数相关的协议,例如将参数移动到堆栈或寄存器。无论函数的大小,协议都存在。因此,内联将删除调用协议(从而减少程序代码大小并提高性能)。

Another reason to inline is to reduce the quantity of function calls. In some processors, a branch instruction (function call), causes the instruction cache (or pipeline) to be reloaded. This takes time. Inlining reduces the function calls and improves execution time.

内联的另一个原因是减少函数调用的数量。在某些处理器中,一个分支指令(函数调用)导致指令缓存(或管道)被重新加载。这需要时间。内联减少了函数调用,提高了执行时间。

Edit 2: Code Bloat
One reason to create functions is to reduce code size. Inlining of large functions may result in code bloat or the increasing of the program's size.

编辑2:代码膨胀创建函数的一个原因是减少代码大小。内联大型函数可能导致代码膨胀或程序的大小增加。

Code bloat and function inlining are under the Time versus Space trade off. Inlining of large functions may speed up execution, but you are trading space for it. Place common code into functions may decrease the size of your program, but take more time to execute.

代码膨胀和功能内联是在时间和空间权衡下进行的。大型函数的内联可能会加速执行,但你是在为它交易空间。将公共代码放置到函数中可以减少程序的大小,但是需要更多的时间来执行。