¿Todavía hay un uso para en línea?

Creí que en inline era obsoleto porque leí aquí :

No importa cómo designe una función como en inline , es una solicitud que el comstackdor puede ignorar: el comstackdor puede expandir en línea algunos, todos o ninguno de los lugares donde llama a una función designada como en inline .

Sin embargo, Angew parece entender algo que yo no. En esta pregunta, él y yo vamos y venimos un poco, sobre si en inline todavía es útil.

Esta pregunta no es una pregunta sobre:

  • El uso histórico de en inline o en inline podría seguir utilizándose para indicar al comstackdor las funciones en inline : ¿ cuándo debería escribir la palabra clave ‘en línea’ para una función / método? .
  • Los beneficios o inconvenientes de inlining function code: Ventajas de las funciones en línea en C ++?
  • Obligar al comstackdor a codificar la función en inline : forzar la función en línea en otra unidad de traducción

Teniendo en cuenta que el comstackdor puede inline a voluntad, en inline no es útil: ¿dónde se puede usar en inline para forzar, no sugerir , un cambio en el código comstackdo?

Trataré de explicar mi “comprensión secreta” de la mejor manera posible.

Aquí hay dos conceptos completamente separados. Una es la capacidad del comstackdor para reemplazar una llamada de función repitiendo el cuerpo de la función directamente en el sitio de llamadas. 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 comstackdor de que debe alinear la función marcada en inline . A medida que los comstackdores mejoraban su optimización, esta funcionalidad se ha ido reduciendo, y usar en inline como sugerencia para alinear una función es obsoleto. El comstackdor lo ignorará alegremente y pondrá en línea algo completamente diferente si considera que es una mejor optimización.

Espero que hayamos tratado con la relación explícita en inline . No hay ninguno en el código actual.

Entonces, ¿cuál es el verdadero propósito 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). Imagina 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 , específicamente le dice al comstackdor y al enlazador: “¡Ustedes chicos, asegúrense 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; } 

Comstackr y vincular estos dos archivos juntos no generará errores de enlazador.

Tenga en cuenta que, por supuesto, la definición de f no tiene que estar en los archivos textualmente. Puede provenir de un archivo de encabezado #include d en su lugar:

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 cabecera, debe marcarla como en inline , de lo contrario, generará múltiples errores de definición.


La última pieza del rompecabezas es: ¿por qué la palabra clave está realmente escrita en inline cuando no tiene nada que ver con la alineación? 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 comstackdor debe tener el cuerpo de la función en primer lugar.

C ++ sigue un modelo de comstackción separado, donde el comstackdor no tiene acceso a archivos de objetos distintos al que está produciendo actualmente. Por lo tanto, para poder alinear una función, su definición debe ser parte de la unidad de traducción actual. Si desea alinearlo en más de una unidad de traducción, su definición debe estar en todas ellas. Normalmente, esto daría lugar a un error de definición múltiple. Por lo tanto, si coloca su función en un encabezado y #include su definición en todas partes para habilitar su alineación en todas partes, debe marcarla como en inline para evitar múltiples errores de definición.

Tenga en cuenta que incluso hoy en día, aunque un comstackdor alineará cualquier función que le parezca adecuada, aún debe tener acceso a la definición de esa función. Por lo tanto, aunque no se requiere la palabra clave en inline como la sugerencia “por favor en línea esto”, aún puede encontrar que necesita usarla para permitir que el comstackdor realice la línea en caso de que decida hacerlo. Sin él, es posible que no pueda obtener la definición en la unidad de traducción, y sin la definición, el comstackdor simplemente no puede alinear la función.

El comstackdor no puede. El enlazador puede Las técnicas de optimización modernas incluyen la generación de código de tiempo de enlace (también conocida como optimización de todo el progtwig), donde el optimizador se ejecuta sobre todos los archivos de objetos como parte del proceso de vinculación, antes de la vinculación real. En este paso, todas las definiciones de funciones están, por supuesto, disponibles y la alineación es perfectamente posible sin que se use una sola palabra clave en inline en ninguna parte del progtwig. Pero esta optimización generalmente es costosa en tiempo de comstackción, especialmente para proyectos grandes. Teniendo esto en cuenta, depender únicamente de LTCG para la alineación puede no ser la mejor opción.


Para completar: he hecho trampas ligeramente en la primera parte. La propiedad ODR en realidad no es propiedad de la palabra clave en inline , sino de las funciones en línea (que es un término del idioma). Las reglas para las funciones en línea son:

  • Puede definirse en varias unidades de traducción sin causar errores del enlazador
  • Debe definirse en cada unidad de traducción en la que se utiliza
  • Todas sus definiciones deben ser token-for-token y entity-for-entity 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 se alinea automáticamente, incluso sin la palabra clave en inline .

inline es principalmente un especificador de vinculación externo ahora, por las razones que usted indicó.

Así que sí, tiene un uso, pero es diferente a las funciones de creación. Le permite definir el mismo método varias veces entre las unidades de comstackción y vincularlas adecuadamente entre sí, 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, obligar a la función de alineación depende del comstackdor, algunos pueden tener soporte a través de attribute específicos o pragma o ( __forceinline ) o lo que sea.

En pocas palabras, le permite definir funciones en los encabezados sin romper el ODR …