¿Cómo funcionan las variables en línea?

En la reunión de estándares Oulu ISO C ++ 2016, el comité de normas votó una propuesta denominada Variables en línea en C ++ 17.

En términos simples, ¿qué son las variables en línea, cómo funcionan y para qué sirven? ¿Cómo deben declararse, definirse y usarse las variables en línea?

La primera frase de la propuesta:

El especificador en inline puede aplicarse tanto a variables como a funciones.

El efecto garantizado de ” inline aplicado a una función es permitir que la función se defina de manera idéntica, con un enlace externo, en múltiples unidades de traducción. Para la práctica, eso significa definir la función en un encabezado, que se puede incluir en múltiples unidades de traducción. La propuesta amplía esta posibilidad a las variables.

Así que, en términos prácticos, la propuesta (ahora aceptada) le permite usar la palabra clave en inline para definir una variable de ámbito de espacio de nombres const vínculo externo, o cualquier miembro de datos de clase static , en un archivo de encabezado, de modo que las múltiples definiciones que resultan cuando ese encabezado está incluido en múltiples unidades de traducción, está bien con el vinculador; simplemente elige una de ellas.

Hasta e incluyendo C ++ 14, la maquinaria interna para esto ha estado allí, para soportar variables static en las plantillas de clase, pero no había una manera conveniente de usar esa maquinaria. Uno tuvo que recurrir a trucos como

 template< class Dummy > struct Kath_ { static std::string const hi; }; template< class Dummy > std::string const Kath_::hi = "Zzzzz..."; using Kath = Kath_; // Allows you to write `Kath::hi`. 

A partir de C ++ 17 y en adelante, creo que uno puede escribir solo

 struct Kath { static std::string const hi; }; inline std::string const Kath::hi = "Zzzzz..."; // Simpler! 

… en un archivo de cabecera.

La propuesta incluye la redacción

Un miembro de datos estáticos en línea se puede definir en la definición de clase y puede especificar un inicializador de llave o igual. Si el miembro se declara con el especificador constexpr , se puede volver a declarar en el ámbito del espacio de nombres sin inicializador (este uso está en desuso, ver DX). Las declaraciones de otros miembros de datos estáticos no deberán especificar un factorizador de paréntesis o igual

… que permite que lo anterior se simplifique aún más a solo

 struct Kath { static inline std::string const hi = "Zzzzz..."; // Simplest! }; 

… como señaló TC en un comentario a esta respuesta.

Además, el especificador ​constexpr implica en inline para miembros de datos estáticos, así como funciones.


Notas:
¹ Para una función en inline también tiene un efecto de alusión sobre la optimización, que el comstackdor debería preferir reemplazar las llamadas de esta función con la sustitución directa del código de la máquina de la función. Esta sugerencia puede ser ignorada.

Las variables en línea son muy similares a las funciones en línea. Indica al vinculador que solo debe existir una instancia de la variable, incluso si la variable se ve en varias unidades de comstackción. El vinculador debe garantizar que no se creen más copias.

Las variables en línea se pueden usar para definir globales en bibliotecas solo de encabezado. Antes de C ++ 17, tenían que usar soluciones temporales (funciones en línea o pirateo de plantillas).

Por ejemplo, una solución es usar el singleton de Meyer con una función en línea:

 inline T& instance() { static T global; return global; } 

Hay algunos inconvenientes con este enfoque, principalmente en términos de rendimiento. Esta sobrecarga podría evitarse mediante soluciones de plantilla, pero es fácil equivocarse.

Con las variables en línea, puede declararlo directamente (sin obtener un error de vinculador de definición múltiple):

 inline T global; 

Además de las bibliotecas de solo encabezado, hay otros casos en los que las variables en línea pueden ayudar. Nir Friedman cubre este tema en su charla en CppCon: Lo que los desarrolladores de C ++ deben saber sobre los globales (y el enlazador) . La parte sobre las variables en línea y las soluciones temporales comienza en 18m9s .

Para abreviar, si necesita declarar las variables globales que se comparten entre las unidades de comstackción, declararlas como variables en línea en el archivo de encabezado es sencillo y evita los problemas con las soluciones anteriores a C ++ 17.

(Todavía hay casos de uso para el singleton de Meyer, por ejemplo, si explícitamente desea tener una inicialización lenta).