¿Cuándo debo escribir la palabra clave inline
para una función / método en C ++?
Después de ver algunas respuestas, algunas preguntas relacionadas:
¿Cuándo no debo escribir la palabra clave 'en línea' para una función / método en C ++?
¿Cuándo no sabrá el compilador cuándo hacer una función / método 'en línea'?
¿Importa si una aplicación es multiproceso cuando se escribe 'en línea' para una función / método?
c++
inline
one-definition-rule
Parcial
fuente
fuente
inline
(9.3 / 2).Respuestas:
Oh hombre, una de mis manías.
inline
es másstatic
oextern
menos una directiva que le dice al compilador que incorpore sus funciones.extern
,static
,inline
Son las directivas de vinculación, que se utilizan casi exclusivamente por el enlazador, no el compilador.Se dice que le
inline
sugiere al compilador que cree que la función debería estar en línea. Eso pudo haber sido cierto en 1998, pero una década después el compilador no necesita tales pistas. Sin mencionar que los humanos generalmente están equivocados cuando se trata de optimizar el código, por lo que la mayoría de los compiladores ignoran la 'pista'.static
- el nombre de la variable / función no se puede usar en otras unidades de traducción. Linker necesita asegurarse de que no utiliza accidentalmente una variable / función estáticamente definida de otra unidad de traducción.extern
- use este nombre de variable / función en esta unidad de traducción pero no se queje si no está definido. El vinculador lo resolverá y se asegurará de que todo el código que intentó utilizar algún símbolo externo tenga su dirección.inline
- esta función se definirá en varias unidades de traducción, no se preocupe por eso. El enlazador debe asegurarse de que todas las unidades de traducción usen una sola instancia de la variable / función.Nota: En general, declarar plantillas no
inline
tiene sentido, ya que ya tienen la semántica de enlaceinline
. Sin embargo, se requiere lainline
especialización explícita y la creación de instancias de plantillas .Respuestas específicas a sus preguntas:
Solo cuando desee que la función se defina en un encabezado. Más exactamente solo cuando la definición de la función puede aparecer en múltiples unidades de traducción. Es una buena idea definir funciones pequeñas (como en un revestimiento) en el archivo de encabezado, ya que le da al compilador más información para trabajar mientras optimiza su código. También aumenta el tiempo de compilación.
No agregue en línea solo porque cree que su código se ejecutará más rápido si el compilador lo alinea.
En general, el compilador podrá hacerlo mejor que usted. Sin embargo, el compilador no tiene la opción de codificar en línea si no tiene la definición de la función. En el código optimizado al máximo, generalmente todos los
private
métodos están en línea, ya sea que lo solicite o no.Como un aparte para evitar la inserción en GCC, use
__attribute__(( noinline ))
, y en Visual Studio, use__declspec(noinline)
.El subprocesamiento múltiple no afecta la alineación de ninguna manera.
fuente
inline
palabra clave no están relacionadas. Sin embargo, tienes la idea correcta. Como regla general, adivinar qué mejoraría si se incluyera en línea es muy propenso a errores. La excepción a esa regla es un revestimiento.Me gustaría contribuir a todas las excelentes respuestas en este hilo con un ejemplo convincente para dispersar cualquier malentendido restante.
Dados dos archivos de origen, como:
inline111.cpp:
inline222.cpp:
Caso A:
Compilar :
Salida :
Discusión :
Incluso si debe tener definiciones idénticas de sus funciones en línea, el compilador de C ++ no lo marca si ese no es el caso (en realidad, debido a la compilación por separado, no tiene formas de verificarlo). ¡Es su propio deber garantizar esto!
Linker no se queja de la regla de una definición , como
fun()
se declara comoinline
. Sin embargo, debido a que inline111.cpp es la primera unidad de traducción (que realmente llamafun()
) procesada por el compilador, el compilador creafun()
una instancia en su primer encuentro de llamada en inline111.cpp . Si el compilador decide no expandirfun()
su llamada desde cualquier otro lugar de su programa ( por ejemplo, desde inline222.cpp ), la llamada afun()
siempre estará vinculada a su instancia producida desde inline111.cpp (la llamada a inline222.cppfun()
interior )también puede producir una instancia en esa unidad de traducción, pero permanecerá desvinculada). De hecho, eso es evidente por las&fun = 0x4029a0
impresiones idénticas .Finalmente, a pesar de la
inline
sugerencia al compilador de expandir realmente el one-linerfun()
, ignora su sugerencia por completo, lo que está claro porquefun() = 111
en ambas líneas.Caso B:
Compilar (aviso de orden inverso) :
Salida :
Discusión :
Este caso se afirma lo que se ha discutido en el caso A .
Observe un punto importante, que si comenta la llamada real
fun()
en inline222.cpp ( por ejemplo ,cout
comente la declaración completa en inline222.cpp ), entonces, a pesar del orden de compilación de sus unidades de traducción,fun()
se instanciará en su primer encuentro de llamada en inline111.cpp , que da como resultado una impresión para el caso B comoinline111: fun() = 111, &fun = 0x402980
.Caso C:
Compilar (aviso -O2) :
o
Salida :
Discusión :
-O2
optimización alienta al compilador a expandir realmente las funciones que se pueden incorporar (tenga en cuenta que también-fno-inline
es predeterminado sin opciones de optimización). Como es evidente por la impresión aquí, enfun()
realidad se ha expandido en línea (de acuerdo con su definición en esa unidad de traducción en particular ), lo que resulta en dos impresiones diferentesfun()
. A pesar de esto, todavía hay solo uno instancia vinculada globalmentefun()
(como lo exige el estándar), como se desprende de una&fun
impresión idéntica .fuente
inline
funciones sean comportamientos indefinidos..cpp
una su propia unidad de traducción. Preferiblemente, agregue casos para-flto
habilitado / deshabilitado.Todavía necesita alinear explícitamente su función al hacer la especialización de plantilla (si la especialización está en el archivo .h)
fuente
1) Hoy en día, casi nunca. Si es una buena idea incorporar una función, el compilador lo hará sin su ayuda.
2) siempre. Ver # 1.
(Editado para reflejar que dividió su pregunta en dos preguntas ...)
fuente
inline
todavía es necesario, por ejemplo, para definir una función en un archivo de encabezado (y eso es necesario para incluir dicha función en varias unidades de compilación).inline
especificador, sus instancias se colapsan automáticamente en una por el vinculador y no se usa ODR.Si la función se declara en la cabecera y definido en el
.cpp
archivo, debería no escribir la palabra clave.No existe tal situación. El compilador no puede hacer una función en línea. Todo lo que puede hacer es alinear algunas o todas las llamadas a la función. No puede hacerlo si no tiene el código de la función (en ese caso, el enlazador debe hacerlo si es capaz de hacerlo).
No, eso no importa en absoluto.
fuente
Esto depende del compilador utilizado. No confíe ciegamente en que hoy en día los compiladores saben mejor que los humanos cómo conectarse en línea y nunca debe usarlo por razones de rendimiento, porque es una directiva de vinculación en lugar de una sugerencia de optimización. Si bien estoy de acuerdo en que estos argumentos son ideológicamente correctos, encontrar la realidad podría ser algo diferente.
Después de leer varios subprocesos, probé por curiosidad los efectos de la línea en el código que estoy trabajando y los resultados fueron que obtuve una aceleración medible para GCC y ninguna aceleración para el compilador Intel.
(Más detalles: simulaciones matemáticas con pocas funciones críticas definidas fuera de clase, GCC 4.6.3 (g ++ -O3), ICC 13.1.0 (icpc -O3); agregar en línea a los puntos críticos causó una aceleración de + 6% con el código GCC).
Entonces, si califica a GCC 4.6 como un compilador moderno, el resultado es que la directiva en línea sigue siendo importante si escribe tareas intensivas de CPU y sabe exactamente dónde está el cuello de botella.
fuente
En realidad, casi nunca. Todo lo que está haciendo es sugerir que el compilador realice una función dada en línea (por ejemplo, reemplace todas las llamadas a esta función / w su cuerpo). No hay garantías, por supuesto: el compilador puede ignorar la directiva.
El compilador generalmente hará un buen trabajo al detectar y optimizar cosas como esta.
fuente
inline
tiene una diferencia semántica en C ++ (por ejemplo, en la forma en que se tratan las definiciones múltiples), lo cual es importante en algunos casos (por ejemplo, plantillas).Verifiqué esto para Visual Studio 9 (15.00.30729.01) compilando con / FAcs y mirando el código de ensamblaje: El compilador produjo llamadas a funciones miembro sin optimización habilitada en modo de depuración . Incluso si la función está marcada con __forceinline , no se genera ningún código de tiempo de ejecución en línea.
fuente
Desea ponerlo al principio, antes de devolver el tipo. Pero la mayoría de los compiladores lo ignoran. Si está definido y tiene un bloque de código más pequeño, la mayoría de los compiladores lo consideran en línea de todos modos.
fuente
A menos que esté escribiendo una biblioteca o tenga razones especiales, puede olvidarse
inline
y utilizar la optimización del tiempo de enlace . Elimina el requisito de que una definición de función debe estar en un encabezado para que se considere su inclusión en las unidades de compilación, que es precisamente lo queinline
permite.(Pero vea ¿Hay alguna razón para no utilizar la optimización del tiempo de enlace? )
fuente
En línea palabra clave en solicita al compilador que reemplace la llamada a la función con el cuerpo de la función, primero evalúa la expresión y luego la pasa. argumentos
Cuándo usar:
fuente
inline
o no en C y C ++. C en línea: stackoverflow.com/a/62287072/7194773 C ++ en línea: stackoverflow.com/a/62230963/7194773C ++ en línea es totalmente diferente a C en línea .
inline
por sí solo afecta al compilador, al ensamblador y al enlazador. Es una directiva para el compilador que dice que solo emite un símbolo para esta función / datos si se usa en la unidad de traducción, y si es así, entonces, como los métodos de clase, dígale al ensamblador que los almacene en la sección.section .text.c::function(),"axG",@progbits,c::function(),comdat
o.section .bss.i,"awG",@nobits,i,comdat
para los datos. Las instancias de plantilla también van en sus propios grupos de comdatos.Esto sigue
.section name, "flags"MG, @type, entsize, GroupName[, linkage]
. Por ejemplo, el nombre de la sección es.text.c::function()
.axG
significa que la sección es asignable, ejecutable y en un grupo, es decir, se especificará un nombre de grupo (y no hay un indicador M, por lo que no se especificará ningún tamaño);@progbits
significa que la sección contiene datos y no está en blanco;c::function()
es el nombre del grupo y el grupo tienecomdat
enlace, lo que significa que en todos los archivos de objetos, todas las secciones encontradas con este nombre de grupo etiquetado con comdat se eliminarán del ejecutable final, excepto 1, es decir, el compilador se asegura de que solo haya una definición en la unidad de traducción y luego le dice al ensamblador que coloque en su propio grupo en el archivo objeto (1 sección en 1 grupo) y luego el vinculador se asegurará de que si algún archivo objeto tiene un grupo con el mismo nombre, solo incluya uno en el archivo .exe final. La diferencia entreinline
y no usarinline
ahora es visible para el ensamblador y, como resultado, para el vinculador, porque el ensamblador no lo almacena de manera regular.data
o.text
etc. debido a sus directivas.static inline
en una clase significa que es una definición de tipo y no una declaración (permite que se defina un miembro estático en la clase) y que sea en línea; ahora se comporta como arriba.static inline
en el alcance del archivo solo afecta al compilador. Significa para el compilador: solo emitir un símbolo para esta función / datos si se usa en la unidad de traducción y hacerlo como un símbolo estático regular (almacenar in.text /.data sin la directiva .globl). Para el ensamblador ahora no hay diferencia entrestatic
ystatic inline
extern inline
es una declaración que significa que debe definir este símbolo en la unidad de traducción o lanzar un error del compilador; si está definido, trátelo como regularinline
y para el ensamblador y el enlazador no habrá diferencia entreextern inline
yinline
, por lo que este es solo un protector del compilador.Todo lo anterior sin la línea de error se contrae
inline int i[5]
. Obviamente, si lo hicieraextern inline int i[] = {5};
,extern
sería ignorado debido a la definición explícita a través de la asignación.inline
en un espacio de nombres, mira esto y estofuente
Al desarrollar y depurar código, omita
inline
. Se complica la depuración.La razón principal para agregarlos es para ayudar a optimizar el código generado. Por lo general, esto cambia el espacio de código aumentado por la velocidad, pero a veces
inline
ahorra espacio de código y tiempo de ejecución.Gastar este tipo de pensamiento sobre la optimización del rendimiento antes de completar el algoritmo es una optimización prematura .
fuente
inline
las funciones generalmente no están integradas a menos que se compilen con optimizaciones, por lo que no afectan la depuración de ninguna manera. Recuerde que es una pista, no una demanda.inline
funciones estaban alineadas. Era imposible establecer un punto de interrupción significativo en ellos.inline
no hará nada para mejorar el código en un compilador moderno, que puede determinar si está en línea o no por sí solo.Cuando uno debe en línea:
1.Cuando se quiere evitar la sobrecarga de cosas que suceden cuando se llama a la función, como pasar parámetros, transferir controles, devolver controles, etc.
2. La función debe ser pequeña, con frecuencia llamada y hacer que la función en línea sea realmente ventajosa, ya que según la regla 80-20, intente hacer que la función esté en línea, lo que tiene un impacto importante en el rendimiento del programa.
Como sabemos, inline es solo una solicitud de compilación similar al registro y le costará el tamaño del código de objeto.
fuente
inline
ha perdido su estado como una pista de optimización, y la mayoría de los compiladores solo lo usan para tener en cuenta las definiciones múltiples, como deberían hacerlo en mi opinión. Más aún, desde C ++ 11,register
ha quedado en desuso por su significado anterior de 'Sé mejor que el compilador cómo optimizar': ahora es solo una palabra reservada sin significado actual.inline
hasta cierto punto.La función en línea de C ++ es un concepto poderoso que se usa comúnmente con las clases. Si una función está en línea, el compilador coloca una copia del código de esa función en cada punto donde se llama a la función en tiempo de compilación.
Cualquier cambio en una función en línea podría requerir que todos los clientes de la función se vuelvan a compilar porque el compilador necesitaría reemplazar todo el código una vez más, de lo contrario, continuará con la funcionalidad anterior.
Para alinear una función, coloque la palabra clave en línea antes del nombre de la función y defina la función antes de realizar cualquier llamada a la función. El compilador puede ignorar el calificador en línea en caso de que la función definida sea más que una línea.
Una definición de función en una definición de clase es una definición de función en línea, incluso sin el uso del especificador en línea.
El siguiente es un ejemplo, que utiliza la función en línea para devolver un máximo de dos números
Para más información ver aquí .
fuente