Exemple exécutable minimal
Cette superbe fonctionnalité C ++ 17 nous permet de:
- utilisez simplement une seule adresse mémoire pour chaque constante
- le stocker en tant que
constexpr
: Comment déclarer constexpr extern?
- faites-le en une seule ligne à partir d'un en-tête
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;
}
Compilez et exécutez:
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 en amont .
Voir aussi: Comment fonctionnent les variables en ligne?
Norme C ++ sur les variables en ligne
Le standard C ++ garantit que les adresses seront les mêmes. C ++ 17 N4659 standard draft
10.1.6 "Le spécificateur en ligne":
6 Une fonction ou une variable en ligne avec un lien externe doit avoir la même adresse dans toutes les unités de traduction.
cppreference https://en.cppreference.com/w/cpp/language/inline explique que si ce static
n'est pas donné, alors il a un lien externe.
Implémentation de variables en ligne GCC
On peut observer comment il est implémenté avec:
nm main.o notmain.o
qui contient:
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
et man nm
dit à propos de u
:
"u" Le symbole est un symbole mondial unique. Il s'agit d'une extension GNU de l'ensemble standard de liaisons de symboles ELF. Pour un tel symbole, l'éditeur de liens dynamique s'assurera que dans l'ensemble du processus, il n'y a qu'un seul symbole avec ce nom et ce type en cours d'utilisation.
nous voyons donc qu'il existe une extension ELF dédiée à cela.
Pré-C ++ 17: extern const
Avant C ++ 17 et en C, nous pouvons obtenir un effet très similaire avec un extern const
, ce qui conduira à l'utilisation d'un seul emplacement mémoire.
Les inconvénients inline
sont:
- il n'est pas possible de faire la variable
constexpr
avec cette technique, inline
permet seulement que: Comment déclarer constexpr extern?
- c'est moins élégant car vous devez déclarer et définir la variable séparément dans l'en-tête et le fichier 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 en amont .
Alternatives d'en-tête pré-C ++ 17 uniquement
Ce ne sont pas aussi bons que la extern
solution, mais ils fonctionnent et n'occupent qu'un seul emplacement mémoire:
Une constexpr
fonction, car constexpr
impliqueinline
et inline
permet (force) la définition d'apparaître sur chaque unité de traduction :
constexpr int shared_inline_constexpr() { return 42; }
et je parie que tout compilateur décent intégrera l'appel.
Vous pouvez également utiliser une variable entière statique const
ou constexpr
comme dans:
#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;
}
mais vous ne pouvez pas faire des choses comme prendre son adresse, ou bien elle devient utilisée odr, voir aussi: https://en.cppreference.com/w/cpp/language/static "Membres statiques constants" et Définition des données statiques constexpr membres
C
En C, la situation est la même que C ++ avant C ++ 17, j'ai téléchargé un exemple sur: Que signifie «statique» en C?
La seule différence est qu'en C ++, cela const
implique static
pour les globaux, mais ce n'est pas le cas en C: C ++ sémantique de `static const` vs` const`
Un moyen de l'intégrer entièrement?
TODO: existe-t-il un moyen d'intégrer complètement la variable, sans utiliser de mémoire du tout?
Tout comme ce que fait le préprocesseur.
Cela nécessiterait en quelque sorte:
- interdire ou détecter si l'adresse de la variable est prise
- ajoutez ces informations aux fichiers objets ELF et laissez LTO l'optimiser
En relation:
Testé dans Ubuntu 18.10, GCC 8.2.0.
const
.