What is the best way of declaring memory efficient Global Constants in C++1z which do not make internal linkage, so a single copy is used though out all the translation units?
Although it's been mentioned in many places, we didn't have any singular "The best approach" question and answer, so here it is. Here is a partial list of places where I have found related questions.
I am looking for a solution for atleast all basic basic types, like int
, double
, char
, std::string
.
Edit 1: By best way, I mean "memory efficient". I know for internal linkage, multiple copies for each translation unit will be made, hence eating memory. Along with that if we can achieve fast execution time and compile time, that will be great, glamorizing (easiness of ) the code doesn't matter. Also I know external linkage will increase the fetching time because the processor may get cache misses at run time. But does latest C++ language support the best approach for all these ? Or may be suggested support for each case ?
Note: Also what will be best approach for std::string
global constants ? Does there mutability will have any affect on there constness ?
In C++17, the easiest way to define a global constant or a global variable is usually with an inline variable. You create one by simply putting a line like the following into your header file:
inline const std::string greeting = "Hello!";
If the global constant is of literal type, prefer using inline constexpr
(the inline
is optional if it’s a static class member) instead of inline const
:
inline constexpr std::string_view greeting = "Hello!"sv;
This also works for variables, but many of the advantages no longer apply, so you might want to use another method:
inline unsigned int score = 0;
First, the two main disadvantages to this method are:
extern
variables, because the compiler needs to consolidate the multiple definitions, and because more information is available to the optimizer. The “somewhat” part turns into a “noticeably” if you might change the definition, but not the data type; since the value is now in a header file, this means recompiling everything that includes the header, instead of just re-linking. If you might need to change the data type everything will need to be recompiled no matter what.If neither of those is important to you, I think this method beats the other ways of getting a global constant with external linkage and at most¹ one definition in the final executable².
An inline variable like this is mentioned in just one file, so it’s easy to change; this is particularly useful for header-only libraries. This also means it has its value available at compile time, so the optimizer can see it and maybe eliminate some usages.
constexpr
In C++17, static constexpr
class members are automatically inline
, so if your global constant should be part of a class’s scope, you can do something like
constexpr int favorite_number = -3;
Otherwise, you will need to say constexpr inline
, which should still work. This will have the semantics described above, but also the usual advantages of constexpr
, so the compiler will know that it can try to do more at compile time. For example:
#include <string_view>
using namespace std::literals;
inline constexpr std::string_view greeting = "Hello!"sv;
inline constexpr int scrabble_points[greeting.size()] = {4, 1, 1, 1, 1, 0};
int main() {
int total = 0;
for (int i : scrabble_points) {
total += i;
}
return total;
}
is possible with constexpr
, but not with just inline
, because with constexpr
it knows that greeting.size()
is a compile-time constant and can be used as the size of an array.³ With optimizations, this could compile to a just a single mov
instruction and ret
, without including any copies of the string or array because it’s unnecessary.
With the new inline semantics, everything before main
could have been in a header file included in multiple places, and there would still have been at most one copy.
The same method easily supports mutable variables by leaving off the const
:
inline std::string player_name = "<subject name here>";
This is then a global variable with external linkage. Since it’s a variable, most of the advantages I’v mentioned over Pete’s answer are gone, but some (like only declaring the variable in one place and not needing to link any thing extra) are still present. They might not be worth the slight extra compile time and the lack of C++14 compatibility, though.
¹ For a const
or constexpr
variable, the compiler/optimizer might completely eliminate the variable if it isn’t needed. In theory, it might decide to copy it to an immediate value or something; in practice you probably shouldn’t worry about that because it would only do that if it had a good reason, and this should make the final executable smaller and/or faster. You could probably tune this with -Os
instead of -O3
.
² Each object file which used the constant would still have a copy, but those would be combined at link time. The only way to avoid that is with extern
variables.
³ This simplified example works even without inline
, or with the array being only const
instead of constexpr
, but those are useful for more complicated real-world situations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With