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(¬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;
}
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 static
no se proporciona, 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 un extern const
, lo que conducirá a que se use una única ubicación de memoria.
Los inconvenientes inline
son:
- no es posible hacer la variable
constexpr
con esta técnica, solo 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(¬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 .
Pre-C ++ 17 encabezado solo alternativas
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 alineará la llamada.
También puede usar una variable entera const
o constexpr
estática 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
C
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 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.
const
.