Puis-je mélanger extern et const, comme const externe? Si oui, le qualificatif const impose-t-il son règne uniquement dans la portée dans laquelle il est déclaré ou doit-il correspondre exactement à la déclaration de l'unité de traduction dans laquelle il est déclaré? C'est à dire. puis-je déclarer dire extern const int i;
même lorsque le réel i n'est pas un const et vice versa?
Le schéma habituel est:
extern const int a_global_var;
#include "file.h"
const int a_global_var = /* some const expression */;
Edit: commentaire de legends2k incorporé. Merci.
Vous pouvez les utiliser ensemble. Mais vous devez être cohérent dans votre utilisation de const car lorsque C++ nomme la décoration, const est inclus dans les informations de type utilisées pour décorer les noms de symboles. donc extern const int i
fera référence à une variable différente de extern int i
Sauf si vous utilisez extern "C" {}. La décoration du nom C ne fait pas attention à la const.
C++ 17 inline
variables
Si vous pensez que vous voulez un extern const
, il est plus probable que vous souhaitiez réellement utiliser C++ 17 inline variables .
Cette superbe fonctionnalité C++ 17 nous permet de:
constexpr
: Comment déclarer constexpr extern?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;
}
Compiler et exécuter:
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
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. projet standard C++ 17 N4659 10.1.6 "Le spécificateur en ligne":
6 Une fonction ou variable en ligne avec liaison 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 static
n'est pas donné, alors il a un lien externe.
Implémentation des variables en ligne
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 global 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 tout le 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 pour cela.
Pré-C++ 17: extern const
extern const
fonctionne comme dans l'exemple ci-dessous, mais les inconvénients par rapport à inline
sont:
constexpr
avec cette technique, seul inline
permet que: Comment déclarer constexpr extern?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
Alternatives à l'en-tête Pre-C++ 17 uniquement
Ce ne sont pas aussi bons que la solution extern
, mais ils fonctionnent et n'occupent qu'un seul emplacement mémoire:
Une fonction constexpr
, car constexpr
implique inline
et inline
permet (force) que la définition apparaisse sur chaque unité de traduction :
constexpr int shared_inline_constexpr() { return 42; }
et je parie que tout compilateur décent alignera l'appel.
Vous pouvez également utiliser une variable d'entier 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, sinon il devient odr-utilisé, voir aussi: https://en.cppreference.com/w/cpp/language/static "Statique constante membres "et Définition des membres de données statiques constexpr
Y a-t-il un moyen de l'intégrer complètement?
TODO: existe-t-il un moyen de complètement aligner la variable, sans utiliser de mémoire du tout?
Tout comme ce que fait le préprocesseur.
Cela nécessiterait en quelque sorte:
En relation:
Testé dans Ubuntu 18.10, GCC 8.2.0.
Vous pouvez les utiliser ensemble et vous pouvez faire toutes sortes de choses qui ignorent le mot clé const, car c'est tout; un mot-clé. Il indique au compilateur que vous ne changerez pas une variable, ce qui à son tour permet au compilateur de faire des optimisations utiles et vous empêche de changer des choses que vous ne vouliez pas.
Possibility.com a n article décent avec un peu plus de fond.