keywords etiquetas ejemplos c++ function inline

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 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 los tiempos de func 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.