C ++ 17 inline
variables
Si buscó en Google "C ++ const static", entonces es muy probable que lo que realmente quiera usar sean las variables en línea de C ++ 17 .
Esta impresionante característica de C ++ 17 nos permite:
- use convenientemente una única dirección de memoria para cada constante
- guárdelo 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(¬main_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 ¬main_i;
}
Compila y ejecuta:
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 .
Consulte también: ¿Cómo funcionan las variables en línea?
Estándar C ++ en variables en línea
El estándar C ++ garantiza que las direcciones serán las mismas. Borrador estándar 10.1.6 de C ++ 17 N4659
"El especificador en línea":
6 Una función o variable en línea con enlace externo deberá tener la misma dirección en todas las unidades de traducción.
cppreference https://en.cppreference.com/w/cpp/language/inline explica que si static
no se proporciona, entonces tiene un enlace externo.
Implementación de variables 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 sobre u
:
"u" El símbolo es un símbolo global único. Esta es una extensión GNU del conjunto estándar de enlaces de símbolos ELF. Para tal símbolo, el enlazador dinámico se asegurará de que en todo el proceso haya solo un símbolo con este nombre y tipo en uso.
por lo que 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 an extern const
, que conducirá a que se utilice una única ubicación de memoria.
Las desventajas inline
son:
- no es posible hacer la variable
constexpr
con esta técnica, solo inline
permite que: ¿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(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.cpp
#include "notmain.hpp"
const int notmain_i = 42;
const int* notmain_func() {
return ¬main_i;
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
extern const int notmain_i;
const int* notmain_func();
#endif
GitHub aguas arriba .
Alternativas de encabezado previo a C ++ 17
Estos no son tan buenos como la extern
solución, pero funcionan y solo ocupan una única ubicación de memoria:
Una constexpr
función, porque constexpr
implicainline
y 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 incluirá la llamada.
También puede usar una variable 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 usa odr, vea también: Definición de miembros de datos estáticos constexpr
C
En C, la situación es la misma que en C ++ antes de C ++ 17, he subido un ejemplo en: ¿Qué significa "estático" en C?
La única diferencia es que en C ++, const
implica static
para 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 en absoluto?
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 objeto ELF y deje que LTO la optimice
Relacionado:
Probado en Ubuntu 18.10, GCC 8.2.0.