tipos significado punteros ejemplos declaracion c++ c++17

c++ - significado - tipos de variables en java ejemplos



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

La primera oración de la propuesta:

" El especificador en inline se puede aplicar tanto a las variables como a las funciones.

El efecto garantizado de inline aplicado a una función es permitir que la función se defina de forma idéntica, con enlaces externos, 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 varias unidades de traducción. La propuesta extiende esta posibilidad a las variables.

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

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

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

Desde C ++ 17 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 encabezado.

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 paréntesis 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; consulte DX). Las declaraciones de otros miembros de datos estáticos no deben especificar un inicializador de llave o igual

... lo 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 indirecto sobre la optimización, que el compilador debería preferir reemplazar las llamadas de esta función con la sustitución directa del código de máquina de la función. Esta sugerencia puede ser ignorada.

En la reunión de estándares 2016 de Oulu ISO C ++, el comité de estándares votó una propuesta llamada Inline Variables 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 utilizarse las variables en línea?


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

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

Por ejemplo, una solución alternativa 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. Las soluciones de plantilla pueden evitar esta sobrecarga, pero es fácil equivocarse.

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

inline T global;

Además de las bibliotecas de encabezado solamente, existen 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 comienza a los 18m9s .

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

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


Ejemplo ejecutable mínimo

Esta increíble característica de C ++ 17 nos permite:

  • utilice convenientemente una sola dirección de memoria para cada constante
  • almacenarlo como constexpr : ¿Cómo declarar constexpr extern?
  • hazlo en una sola línea desde un encabezado

main.cpp

#include <cassert> #include "notmain.hpp" int main() { // Both files see the same memory address. assert(&notmain_i == notmain_func()); assert(notmain_i == 42); }

notmain.hpp

#ifndef NOTMAIN_HPP #define NOTMAIN_HPP inline constexpr int notmain_i = 42; const int* notmain_func(); #endif

notmain.cpp

#include "notmain.hpp" const int* notmain_func() { return &notmain_i; }

Compilar y ejecutar:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o ./main

GitHub aguas arriba .

Ver también: ¿Cómo funcionan las variables en línea?

C ++ estándar en variables en línea

El estándar C ++ garantiza que las direcciones serán las mismas. C ++ 17 N4659 borrador estándar 10.1.6 "El especificador en línea":

6 Una función o variable en línea con enlace externo tendrá la misma dirección en todas las unidades de traducción.

cppreference https://en.cppreference.com/w/cpp/language/inline explica que si no se proporciona static , entonces tiene un enlace externo.

Implementación variable en línea de GCC

Podemos observar cómo se implementa con:

nm main.o notmain.o

que contiene:

main.o: U _GLOBAL_OFFSET_TABLE_ U _Z12notmain_funcv 0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__ U __assert_fail 0000000000000000 T main 0000000000000000 u notmain_i notmain.o: 0000000000000000 T _Z12notmain_funcv 0000000000000000 u notmain_i

y man nm dice acerca de u :

"u" El símbolo es un símbolo global único. Esta es una extensión de GNU para el conjunto estándar de enlaces de símbolos ELF. Para dicho símbolo, el enlazador dinámico se asegurará de que en todo el proceso solo haya un símbolo con este nombre y tipo en uso.

entonces vemos que hay una extensión ELF dedicada para esto.

Pre-C ++ 17: extern const

Antes de C ++ 17, y en C, podemos lograr un efecto muy similar con una extern const , lo que conducirá a que se use una única ubicación de memoria.

Las desventajas sobre en inline son:

  • no es posible hacer la variable constexpr con esta técnica, solo en inline permite eso: ¿Cómo declarar constexpr extern?
  • es menos elegante ya que debe declarar y definir la variable por separado en el encabezado y el archivo cpp

main.cpp

#include <cassert> #include "notmain.hpp" int main() { // Both files see the same memory address. assert(&notmain_i == notmain_func()); assert(notmain_i == 42); }

notmain.cpp

#include "notmain.hpp" const int notmain_i = 42; const int* notmain_func() { return &notmain_i; }

notmain.hpp

#ifndef NOTMAIN_HPP #define NOTMAIN_HPP extern const int notmain_i; const int* notmain_func(); #endif

GitHub aguas arriba .

Pre-C ++ 17 encabezado solo alternativas

Estos no son tan buenos como la solución extern , pero funcionan y solo ocupan una única ubicación de memoria:

Una función constexpr , porque constexpr implica en inline e inline permite (fuerza) que la definición aparezca en cada unidad de traducción :

constexpr int shared_inline_constexpr() { return 42; }

y apuesto a que cualquier compilador decente alineará la llamada.

También puede usar una variable entera estática const o constexpr como en:

#include <iostream> struct MyClass { static constexpr int i = 42; }; int main() { std::cout << MyClass::i << std::endl; // undefined reference to `MyClass::i'' //std::cout << &MyClass::i << std::endl; }

pero no puede hacer cosas como tomar su dirección, o de lo contrario se usará odr, vea también: https://en.cppreference.com/w/cpp/language/static "Miembros estáticos constantes" y Definiendo datos estáticos constexpr miembros

do

En C la situación es la misma que C ++ pre C ++ 17, he subido un ejemplo en: ¿Qué significa "estático" en C?

La única diferencia es que en C ++, const implica static para los globales, pero no en C: C ++ semántica de `static const` vs` const`

¿Alguna forma de alinearlo completamente?

TODO: ¿hay alguna forma de alinear completamente la variable, sin usar memoria alguna?

Muy parecido a lo que hace el preprocesador.

Esto requeriría de alguna manera:

  • prohibir o detectar si se toma la dirección de la variable
  • agregue esa información a los archivos de objetos ELF y deje que LTO la optimice

Relacionado:

Probado en Ubuntu 18.10, GCC 8.2.0.