Globale Konstanten in C ++ 11
Was sind die besten Möglichkeiten, globale Konstanten in C ++ zu deklarieren und zu definieren? Ich interessiere mich hauptsächlich für den C ++ 11-Standard, da er in dieser Hinsicht viele Fehler behebt.
[EDIT (Klarstellung)]: In dieser Frage bezeichnet "globale Konstante" eine konstante Variable oder Funktion, die zur Kompilierungszeit in bekannt istirgendein Umfang. Die globale Konstante muss von mehr als einer Übersetzungseinheit aus zugänglich sein. Es ist nicht unbedingt eine Konstante im constexpr-Stil - kann so etwas seinconst std::map<int, std::string> m = { { 1, "U" }, { 5, "V" } };
oderconst std::map<int, std::string> * mAddr() { return & m; }
. Ich rühre in dieser Frage nicht den bevorzugten Gültigkeitsbereich oder Namen für die Konstante an. Lassen Sie uns diese Fragen für eine andere Frage belassen. [END_EDIT]
Ich möchte Antworten für all die verschiedenen Fälle wissen, also lassen Sie uns das annehmenT
ist eine der folgenden:
typedef int T; // 1
typedef long double T; // 2
typedef std::array<char, 1> T; // 3
typedef std::array<long, 1000> T; // 4
typedef std::string T; // 5
typedef QString T; // 6
class T {
// unspecified amount of code
}; // 7
// Something special
// not mentioned above? // 8
Ich glaube, dass es kein großes gibtsemantisch (Ich diskutiere hier nicht über gute Benennung oder Gültigkeitsbereich) Unterschied zwischen den 3 möglichen Gültigkeitsbereichen:
// header.hpp
extern const T tv;
T tf(); // Global
namespace Nm {
extern const T tv;
T tf(); // Namespace
}
struct Cl {
static const T tv;
static T tf(); // Class
};
Wenn jedoch die Wahl eines besseren Weges aus den unten stehenden Alternativen vom Unterschied zwischen den oben genannten Deklarationsbereichen abhängt, weisen Sie darauf hin.
Betrachten Sie auch den Fall, wenn ein Funktionsaufruf in einer konstanten Definition verwendet wird, z.<some value>==f();
. Wie würde der Aufruf einer Funktion in ständiger Initialisierung die Wahl zwischen Alternativen beeinflussen?
Lass uns in Erwägung ziehenT
mitconstexpr
Konstruktor zuerst. Offensichtliche Alternativen sind:
// header.hpp
namespace Ns {
constexpr T A = <some value>;
constexpr T B() { return <some value>; }
inline const T & C() { static constexpr T t = <some value>; return t; }
const T & D();
}
// source.cpp
const T & Ns::D() { static constexpr T t = <some value>; return t; }
Ich glaube dasA
undB
sind am besten für kleine geeignetT
(so dass das Vorhandensein mehrerer Instanzen oder das Kopieren zur Laufzeit kein Problem darstellt), z.1-3
, manchmal7
. C
undD
sind besser wennT
ist groß, z.B.4
, manchmal7
.
T
ohneconstexpr
Konstrukteur. Alternativen:
// header.hpp
namespace Ns {
extern const T a;
inline T b() { return <some value>; }
inline const T & c() { static const T t = <some value>; return t; }
const T & d();
}
// source.cpp
extern const T Ns::a = <some value>;
const T & Ns::d() { static const T t = <some value>; return t; }
Ich würde normalerweise nicht verwendena
wegen statischen Initialisierungsreihenfolge Fiasko. Soweit ich weiss,b
, c
undd
sind absolut sicher, sogar threadsicher seit C ++ 11.b
scheint keine gute Wahl zu sein, es sei dennT
hat einen sehr billigen Konstruktor, was für Nicht-Constexpr-Konstruktoren ungewöhnlich ist. Ich kann einen Vorteil nennenc
Überd
- kein Funktionsaufruf (Laufzeitleistung); Ein Vorteil vond
Überc
- Weniger Neukompilierung, wenn der Wert der Konstante geändert wird (diese Vorteile gelten auch fürC
undD
). Ich bin mir sicher, dass ich hier viele Argumente verpasst habe. Geben Sie bitte andere Überlegungen in den Antworten an.
Wenn Sie den obigen Code ändern / testen möchten, können Sie meine Testdateien verwenden (nur header.hpp, source.cpp mit kompilierbaren Versionen der obigen Codefragmente und main.cpp, die Konstanten aus header.hpp ausgeben):https://docs.google.com/uc?export=download&id=0B0F-aqLyFk_PVUtSRnZWWnd4Tjg