c++ - ¿Todavía hay un uso para en línea?
function methods (2)
Trataré de explicar mi "comprensión secreta" de la mejor manera que pueda.
Hay dos conceptos completamente separados aquí.
Una es la capacidad del compilador para reemplazar una llamada a la función repitiendo el cuerpo de la función directamente en el sitio de la llamada.
La otra es la posibilidad de definir una función en más de una unidad de traducción (= más de un archivo
.cpp
).
El primero se llama función en línea.
El segundo es el propósito de la palabra clave en
inline
.
Históricamente,
la palabra clave en
inline
también fue una fuerte sugerencia para el compilador de que debería alinear la función marcada en
inline
.
A medida que los compiladores se volvieron mejores en la optimización, esta funcionalidad ha retrocedido, y el uso de en
inline
como una sugerencia para incorporar una función es obsoleto.
El compilador felizmente lo ignorará e incorporará algo completamente diferente si encuentra que es una mejor optimización.
Espero que hayamos tratado con la relación explícita en
inline
inclinación.
No hay ninguno en el código actual.
Entonces, ¿cuál es el propósito real de la palabra clave en
inline
?
Es simple: una función marcada en
inline
se puede definir en más de una unidad de traducción sin violar la Regla de una definición (ODR).
Imagine estos dos archivos:
file1.cpp
int f() { return 42; }
int main()
{ return f(); }
file2.cpp
int f() { return 42; }
Este comando:
> gcc file1.cpp file2.cpp
Producirá un error de enlazador, quejándose de que el símbolo
f
se define dos veces.
Sin embargo, si marca una función con la palabra clave en
inline
, le dice específicamente al compilador y al enlazador: "¡Ustedes se aseguran de que múltiples definiciones idénticas de esta función no
den
lugar a ningún error!"
Entonces lo siguiente funcionará:
file1.cpp
inline int f() { return 42; }
int main()
{ return f(); }
file2.cpp
inline int f() { return 42; }
Compilar y vincular estos dos archivos juntos no producirá ningún error de vinculador.
Tenga en cuenta que, por supuesto, la definición de
f
no tiene que estar literalmente en los archivos.
En su lugar, puede provenir de un archivo de encabezado
#include
d:
f.hpp
inline int f() { return 42; }
file1.cpp
#include "f.hpp"
int main()
{ return f(); }
file2.cpp
#include "f.hpp"
Básicamente, para poder escribir una definición de función en un archivo de encabezado, debe marcarla como en
inline
, de lo contrario, se generarán múltiples errores de definición.
La última pieza del rompecabezas es: ¿por qué la palabra clave realmente se escribe en
inline
cuando no tiene nada que ver con la inlínea?
La razón es simple: para alinear una función (es decir, para reemplazar una llamada repitiendo su cuerpo en el sitio de la llamada), el compilador debe
tener
el cuerpo de la función en primer lugar.
C ++ sigue un modelo de compilación separado, donde el
compilador
no tiene acceso a archivos de objetos que no sean el que está produciendo actualmente.
Por lo tanto, para poder en línea una función, su definición debe ser parte de la unidad de traducción actual.
Si desea poder incorporarlo en más de una unidad de traducción, su definición debe estar en todas ellas.
Normalmente, esto llevaría a un error de definición múltiple.
Entonces, si coloca su función en un encabezado e
#include
su definición en todas partes para habilitar su alineación en todas partes, debe marcarla como en
inline
para evitar errores de definición múltiple.
Tenga en cuenta que incluso hoy, aunque un compilador incorporará cualquier función que considere adecuada, aún debe tener acceso a la definición de esa función.
Por lo tanto, aunque la palabra clave en
inline
no es necesaria ya que la sugerencia "por favor, incluya esto", es posible que todavía necesite usarla para
permitir que
el compilador haga la línea si así lo desea.
Sin ella, es posible que no pueda obtener la definición en la unidad de traducción, y sin la definición, el compilador simplemente no puede alinear la función.
El compilador no puede.
El enlazador puede.
Las técnicas de optimización modernas incluyen Generación de código de tiempo de enlace (también conocido como Optimización de todo el programa), donde el optimizador se ejecuta sobre todos los archivos de objetos como parte del proceso de enlace, antes del enlace real.
En este paso, todas las definiciones de funciones están disponibles, por supuesto, y la alineación es perfectamente posible sin que se use una sola palabra clave en
inline
en cualquier parte del programa.
Pero esta optimización es generalmente costosa en tiempo de construcción, especialmente para proyectos grandes.
Con esto en mente, depender únicamente de LTCG para la alineación puede no ser la mejor opción.
Para completar: he hecho un poco de trampa en la primera parte.
La propiedad ODR no es en realidad una propiedad de la palabra clave en
inline
, sino de
funciones en línea
(que es un término del lenguaje).
Las reglas para las funciones en línea son:
- Se puede definir en varias unidades de traducción sin causar errores de enlace
- Debe definirse en cada unidad de traducción en la que se utiliza
- Todas sus definiciones deben ser token por token y entidad por entidad idénticas
La palabra clave en
inline
convierte una función en una función en línea.
Otra forma de marcar una función como en línea es definirla (no solo declararla) directamente en una definición de clase.
Dicha función está en línea automáticamente, incluso sin la palabra clave en
inline
.
Esta pregunta ya tiene una respuesta aquí:
Creí, en
inline
era obsoleto porque leí
here
:
No importa cómo designe una función como en
inline
, es una solicitud que el compilador puede ignorar: el compilador puede expandir en línea algunos, todos o ninguno de los lugares donde llama a una función designada como eninline
.
Sin embargo,
Angew
parece entender algo que yo no.
En
esta pregunta,
él y yo vamos y venimos un poco, sobre si en
inline
sigue siendo útil.
Esta pregunta no es una pregunta sobre:
-
El uso histórico de
inline
o dondeinline
todavía se podría usar para indicar al compilador queinline
funciones: ¿ Cuándo debo escribir la palabra clave ''inline'' para una función / método? . - Los beneficios o inconvenientes del código de función en línea : ¿ Beneficios de las funciones en línea en C ++?
-
Obligar al compilador al código de función en
inline
: forzar la función en línea en otra unidad de traducción
Teniendo en cuenta que el compilador puede en
inline
a voluntad, por lo que no es útil en
inline
:
¿Dónde se puede usar en
inline
para forzar,
no sugerir
, un cambio en el código compilado?
inline
es principalmente un especificador de vinculación externo, por las razones que usted indicó.
Entonces, sí, tiene un uso, pero es diferente de las funciones realmente en línea. Le permite definir el mismo método varias veces entre unidades de compilación y vincularlas correctamente, en lugar de obtener múltiples errores de definición.
//header.h
inline void foo() {}
void goo() {}
//cpp1.cpp
#include "header.h"
//cpp2.cpp
#include "header.h"
// foo is okay, goo breaks the one definition rule (ODR)
En realidad, forzar la función en línea depende del compilador, algunos pueden tener soporte a través de
attribute
específicos o
pragma
s o (
__forceinline
) o cualquier otra cosa.
En pocas palabras, le permite definir funciones en encabezados sin romper el ODR ...