Esempio minimo eseguibile
Questa fantastica funzionalità C ++ 17 ci consente di:
- utilizzare convenientemente un solo indirizzo di memoria per ogni costante
- conservalo come
constexpr
: Come dichiarare constexpr extern?
- fallo in una singola riga da un'intestazione
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 ed esegui:
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 a monte .
Guarda anche: Come funzionano le variabili in linea?
Standard C ++ sulle variabili inline
Lo standard C ++ garantisce che gli indirizzi saranno gli stessi. C ++ 17 N4659 bozza standard
10.1.6 "The inline specifier":
6 Una funzione o una variabile inline con collegamento esterno deve avere lo stesso indirizzo in tutte le unità di traduzione.
cppreference https://en.cppreference.com/w/cpp/language/inline spiega che se static
non viene fornito, ha un collegamento esterno.
Implementazione delle variabili inline di GCC
Possiamo osservare come viene implementato con:
nm main.o notmain.o
che 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
e man nm
dice di u
:
"u" Il simbolo è un simbolo globale univoco. Questa è un'estensione GNU al set standard di associazioni di simboli ELF. Per un tale simbolo, il linker dinamico farà in modo che nell'intero processo ci sia solo un simbolo con questo nome e tipo in uso.
quindi vediamo che esiste un'estensione ELF dedicata per questo.
Pre-C ++ 17: extern const
Prima di C ++ 17 e in C, possiamo ottenere un effetto molto simile con un extern const
, che porterà all'uso di un'unica posizione di memoria.
Gli svantaggi inline
sono:
- non è possibile creare la variabile
constexpr
con questa tecnica, inline
consente solo che: Come dichiarare constexpr extern?
- è meno elegante in quanto devi dichiarare e definire la variabile separatamente nell'header e nel file 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 a monte .
Alternative solo all'intestazione pre-C ++ 17
Questi non sono buoni come la extern
soluzione, ma funzionano e occupano solo una singola posizione di memoria:
Una constexpr
funzione, perché constexpr
implicainline
e inline
permette (forza) che la definizione compaia su ogni unità di traduzione :
constexpr int shared_inline_constexpr() { return 42; }
e scommetto che qualsiasi compilatore decente integrerà la chiamata.
Puoi anche usare una const
o constexpr
una variabile intera statica come in:
#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;
}
ma non puoi fare cose come prendere il suo indirizzo, altrimenti diventa odr-used, vedi anche: https://en.cppreference.com/w/cpp/language/static "Constant static members" e Defining constexpr static data membri
C
In C la situazione è la stessa di C ++ pre C ++ 17, ho caricato un esempio su: Cosa significa "statico" in C?
L'unica differenza è che in C ++, const
implica static
per le globali, ma non nella semantica C: C ++ di `static const` vs` const`
Qualche modo per integrarlo completamente?
TODO: esiste un modo per incorporare completamente la variabile, senza utilizzare alcuna memoria?
Molto simile a ciò che fa il preprocessore.
Ciò richiederebbe in qualche modo:
- proibire o rilevare se l'indirizzo della variabile è preso
- aggiungi queste informazioni ai file oggetto ELF e lascia che LTO le ottimizzi
Relazionato:
Testato in Ubuntu 18.10, GCC 8.2.0.
const
.