c++ - etiquetas - Funciones en línea: ¿qué son exactamente frente a la palabra clave en línea?
meta keywords ejemplos (3)
El punto 1) significa que puedo dar una implementación diferente siempre que estén en diferentes unidades de traducción
No, dice que puedes tener más de una implementación. No dice que pueden ser diferentes. Las implementaciones deben ser todas idénticas.
Estoy desconcertado en el caso de que tenga un archivo fuente
source.cc
con una declaración parafunc
y un archivo de cabecera con otra declaración defunc
La unidad de traducción es el parsource.cc+header.h
y en tal caso haber declarado dos los tiempos defunc
no tienen ningún sentido, ¿es así?
Puede declarar una función tantas veces como desee, en tantas unidades de traducción como desee, independientemente de si está en línea o no. Inline no es un factor aquí.
2) La definición de una función o variable en línea (ya que C ++ 17) debe estar presente en la unidad de traducción a la que se accede.
Este es el caso habitual en el que separe la definición de la declaración, la primera en un archivo de encabezado, la segunda en el archivo fuente, si necesito usar la función, debo incluir solo el encabezado, ¿verdad? El punto de acceso sería proporcionado por la fuente durante la fase de enlace, ¿correcto?
No, la definición de una función en línea debe estar presente en cada TU que la usa, antes de la fase de enlace. El propósito de las funciones en línea es permitir definiciones en múltiples TU; utilizas en inline
cuando quieres poner la definición de una función en un encabezado.
El caso 3) establece que la palabra clave en línea es obligatoria a menos que la función que se declarará sea estática.
No, no dice eso en absoluto, no sé cómo pudiste haberlo interpretado de esa manera. Simplemente dice que una función static
inline
tiene una vinculación interna, y una función no static
inline
tiene una vinculación externa, y los puntos 3.1 y 3.2 se aplican a las funciones en inline
con la vinculación externa.
En la práctica, una función debe estar en línea cuando tal función es muy pequeña, pero no siempre el compilador alineará la función declarada como en línea, por ejemplo, si tiene bucles dentro o recursión (estados efectivos de C ++). En general, depende del compilador, me pregunto ahora ...
Supongamos que tengo dos funciones: la primera es autónoma (no llama internamente a ninguna otra función), la segunda es la primera (puede suponer que ambas son 10 líneas por razones de argumento). ¿Deberían ambos declararse en línea? ¿deberían declararse en un archivo de encabezado? ¿o debería separar la definición en un archivo de cabecera y la implementación en un archivo fuente? ¿Qué sería mejor?
Si el optimizador realizará o no la sustitución en línea de un cuerpo de función no está fuertemente correlacionado con si se trata de una función en inline
. El optimizador determinará por sí solo si se realizará una sustitución en línea de una función, independientemente de si se trata de una función en inline
o no. Usted declara las funciones en inline
si quiere poner su definición en un encabezado.
En este enlace , se explica qué es una función en línea y cuál es la palabra clave en línea. Lo estoy leyendo porque me di cuenta de que nunca había entendido el significado de estos dos conceptos y cómo deberían usarse en la práctica. Estoy citando y comentando desde el enlace que brindé
Una función en línea o variable en línea (desde C ++ 17) es una función o variable (desde C ++ 17) con las siguientes propiedades:
1) Puede haber más de una definición de una función o variable en línea (desde C ++ 17) en el programa, siempre que cada definición aparezca en una unidad de traducción diferente. Por ejemplo, una función en línea o una variable en línea (desde C ++ 17) se puede definir en un archivo de encabezado que se incluye en múltiples archivos de origen.
Aquí ya tengo problemas de comprensión, la declaración es la especificación de nuevos identificadores como
void func(void);
mientras que una definición es la implementación real, incluido el cuerpo
void func(void) {
//some code...
}
El punto 1) significa que puedo dar una implementación diferente siempre que estén en diferentes unidades de traducción (es decir, una implementación por encabezado e por cada archivo de origen), pero estoy desconcertado en el caso de que tenga un archivo fuente source.cc
con una declaración para func
y un archivo de cabecera con otra declaración de func
la unidad de traducción es el par source.cc+header.h
y en tal caso haber declarado dos veces func
no tiene ningún sentido, ¿es así?
2) La definición de una función o variable en línea (desde C ++ 17) debe estar presente en la unidad de traducción a la que se accede (no necesariamente antes del punto de acceso).
Este es el caso habitual en el que separe la definición de la declaración, la primera en un archivo de encabezado, la segunda en el archivo fuente, si necesito usar la función, debo incluir solo el encabezado, ¿verdad? El punto de acceso sería proporcionado por la fuente durante la fase de enlace, ¿correcto?
3) Una función o variable en línea (desde C ++ 17) con enlaces externos (por ejemplo, no estática declarada) tiene las siguientes propiedades adicionales: 1) Debe declararse en línea en cada unidad de traducción. 2) Tiene la misma dirección en cada unidad de traducción.
¿Podría darnos un ejemplo simple de lo que esto significa? No puedo imaginar un caso práctico de tal caso. El caso 3) establece que la palabra clave en inline
es obligatoria a menos que la función que se declarará sea estática.
¿Todo lo que dije hasta ahora es correcto?
En la práctica, una función debe estar en línea cuando tal función es muy pequeña, pero no siempre el compilador alineará la función declarada como en línea, por ejemplo, si tiene bucles dentro o recursión (estados efectivos de C ++). En general, depende del compilador, me pregunto ahora ...
Supongamos que tengo dos funciones: la primera es autónoma (no llama internamente a ninguna otra función), la segunda es la primera (puede suponer que ambas son 10 líneas por razones de argumento). ¿Deberían ambos declararse en línea? ¿deberían declararse en un archivo de encabezado? ¿o debería separar la definición en un archivo de cabecera y la implementación en un archivo fuente? ¿Qué sería mejor?
Editar 1 :
Seguir una de las respuestas es mejor si trabajo con ejemplos, con análisis de código ensamblador relacionado.
Eliminé el código anterior porque no tenía sentido (la optimización del indicador -O3
no estaba configurada).
Comienzo de nuevo ... Tengo 5 archivos header.h
, src.cc
, src1.cc
, src2.cc
y main.cc
Para cada unidad de traducción, se publica el código de conjunto relacionado.
He manipulado esos archivos de tres formas diferentes y luego observé el código ensamblado generado, esto me ayudó a entender cómo funciona la palabra clave en línea.
Ejemplo 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();
}
Asamblea 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
red eléctrica
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
Ejemplo 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();
}
Asamblea 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
red eléctrica
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
Ejemplo 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();
}
Asamblea 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
red eléctrica
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
El ejemplo 1 y el ejemplo 3 son los que me interesan particularmente, porque deberían resaltar de alguna manera cuál es la diferencia entre una función en línea y una función no en línea (siguiendo los puntos 1,2 y 3 del enlace que publiqué anteriormente), No veo ninguna falta de propiedades en las funciones no en línea en comparación con la versión en línea. ¿Alguien puede resaltar la diferencia para mí (nuevamente en términos de los puntos 1,2 y 3)?
Quizás algunos ejemplos ayudarían.
1. Biblioteca compilada tradicional
foo.h:
extern int x;
int * f();
foo.cpp:
#include "foo.h"
int x = 25;
int * f() { return &x; }
Los usuarios incluyen foo.h
y necesitan enlazar en la unidad de traducción que contiene foo.cpp
para llamar a f
. Cada llamada devuelve la misma dirección.
2. Separar, variables TU-locales
foo.h:
static int x = 35;
static int * f() { return &x; }
Cada TU que incluye foo.h
obtiene una función independiente y distinta f
, que da como resultado un valor único por TU.
3. Violación del primer ODR del bebé
foo.h:
static int x = 45;
inline int * f() { return &x; }
Esto parece ser una biblioteca de solo encabezado, pero si foo.h
está incluido en más de una TU, esto constituye una violación de ODR, ya que f
se definiría más de una vez, pero no todas sus definiciones serían idénticas.
Este es un error común. Las soluciones incluyen cosas como hacer x
una plantilla o reemplazar x
con una función como int & x() { static int impl = 45; return impl; }
int & x() { static int impl = 45; return impl; }
int & x() { static int impl = 45; return impl; }
. Tenga en cuenta que si omite la static
, lo más probable es que obtenga un error del enlazador debido a múltiples definiciones de x
; static
aparentemente "hace que el código compile".
4. C ++ 17 al rescate: bibliotecas adecuadas solo de encabezado
foo.h:
inline int x = 55;
inline int * f() { return &x; }
Esta versión es funcionalmente equivalente a (1), pero no requiere una unidad de traducción dedicada para contener las definiciones de x
y f
.
Vamos a poner el tema de si en inline
está forzado o no, de lado por el momento (hay muchas discusiones sobre el tema).
Alinear una función es equivalente a pegar el contenido de la función en la ubicación de la llamada a la función (invocación).
Entonces, dado lo siguiente:
void Hello()
{
std::cout << "Hello/n";
}
int main()
{
Hello();
return 0;
}
Cuando la función Hello
está en línea, obtendrá el equivalente de:
int main()
{
// Hello();
std::cout << "Hello/n"; // This is the content of function Hello().
return 0;
}
El compilador tiene permiso para alinear funciones que no están marcadas como inlineadas. Esta característica a menudo se desencadena por una configuración de optimización.
Edición 1: Motivo común para alinear
Una razón común para establecer una función es cuando el contenido es menor o igual que la sobrecarga para llamar a la función.
Hay un protocolo asociado con la función call a, como mover parámetros a la pila o registros. El protocolo existe independientemente del tamaño de la función. Por lo tanto, inline eliminará el protocolo de llamada (reduciendo así el tamaño del código del programa y aumentando el rendimiento).
Otra razón para inline es reducir la cantidad de llamadas a funciones. En algunos procesadores, una instrucción de bifurcación (llamada de función) hace que la memoria caché de instrucciones (o canalización) se vuelva a cargar. Esto lleva tiempo. Inline reduce las llamadas de función y mejora el tiempo de ejecución.
Edit 2: Code Bloat
Una razón para crear funciones es reducir el tamaño del código. La inclusión de funciones grandes puede ocasionar la saturación del código o el aumento del tamaño del programa.
La inflexión de código y la alineación de función están en el intercambio de tiempo versus espacio . La inclusión de funciones grandes puede acelerar la ejecución, pero usted está negociando espacio para ello. Colocar código común en las funciones puede disminuir el tamaño de su programa, pero tomar más tiempo para ejecutar.