C ++ 17 inlinevariables
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 staticno 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 nmdice 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 inlineson:
- no es posible hacer la variable
constexprcon esta técnica, solo inlinepermite 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 externsolución, pero funcionan y solo ocupan una única ubicación de memoria:
Una constexprfunción, porque constexprimplicainline 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 consto constexprcomo 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 ++, constimplica staticpara 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.