37
votes

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:

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.

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?

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).

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?

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.

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.

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...

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?

Edit 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).

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.

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:

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();
}

Assembly 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

Example 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();
}

Assembly 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

Example 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();
}

Assembly 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

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)?

3
Imagine you say #include "source.cc" in multiple translation units.Kerrek SB
You can have multiple implementations of an inline function in C++, but they cannot be different from each other. C++ explicitly requires all the definitions of an inline function to be identical. C differs in that regard, however.John Bollinger
To be honest I still don't understand the difference with a normal function (based on the definition given of inline function). Where is the difference? I'm assuming the difference is that a normal function doesn't have to be in each translation unit.user8469759

3 Answers

44
votes

Maybe a few examples would help.

1. Traditional compiled library

foo.h:

extern int x;
int * f();

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.

2. Separate, TU-local variables

foo.h:

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.

3. Baby's first ODR violation

foo.h:

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.

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".

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

foo.h:

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.

15
votes

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

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?

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.

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?

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.

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

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.

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...

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?

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.

8
votes

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:

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.

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.

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.