Defining global constants in C ++ 1z?

What is the best way to declare efficient global global constants in C ++ 1z that do not do internal binding , so one copy is used despite all translation units ?

Although it was mentioned in many places, we did not have a single question “Best approach” and an answer, so here it is. Here is an incomplete list of places where I found related questions.

  • constexpr global constants in the header file and odr
  • global declarations / initializations using static, const, constexpr
  • Global constants in C ++ 11
  • Defining a global constant in C ++
  • C ++ global constants problem
  • Global constants against enumeration
  • The right way to create a global "constant" in C ++

I am looking for a solution for at least all of the basic base types, such as int , double , char , std::string .

Edit 1: Best of all, I mean "memory efficiency." I know, for internal communication, several copies will be made for each unit of translation, therefore, there is memory. Along with this, if we can achieve fast execution time and compilation time, it will be great, the glamor (simplicity) of the code does not matter. I also know that external communication will increase the reception time because the processor may receive cache misses at runtime. But does the latest C ++ language support a better approach to all of these? Or can support be offered for every occasion?

Note: And what would be the best approach for std::string global constants? Can variability affect a constant?

+7
c ++ constants c ++ 17
source share
3 answers

Summary

In C ++ 17, the easiest way to define a global constant or global variable is usually to have a built-in variable . You create it by simply pasting the following line into your header file:

 inline const std::string greeting = "Hello!"; 

If the global constant is of literal type , prefer to use inline constexpr ( inline is optional if its 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 benefits no longer apply, so you can use a different method:

 inline unsigned int score = 0; 

More details

Firstly, there are two main advantages of dis for this method:

  • It does not work until C ++ 17, so if you need compatibility with C ++ 14 or earlier, you need to do something else. The stunt template that VTT offers is compatible with old standards and should have similar semantics for built-in variables, but it is a hack that is no longer needed if you only need C ++ 17.
  • Harder to compile than

If none of them is important to you, I think that this method is superior to other methods of obtaining a global constant with external reference and, for the most part, one definition in the final executable file.

A built-in variable like this is mentioned in only one file, so it is easy to change it; this is especially useful for header-only libraries. It also means that it has a value available at compile time, so the optimizer can see it and possibly eliminate some of the usage.

Using constexpr

In C ++ 17, the static members of the constexpr class constexpr automatically inline , so if your global constant should be part of the class scope, you can do something like

 constexpr int favorite_number = -3; 

Otherwise, you will need to tell constexpr inline , which should work. This will have the semantics described above, but also the usual benefits 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; } 

possible with constexpr , but not only with inline , because with constexpr it knows that greeting.size() is a compile-time constant and can be used as the size of the array. 3 With optimization, this can compile only one mov and ret statement without including any copies of the string or array, because it is not needed.

With the new built-in semantics, everything that came before main could be in the header file included in several places, and still there would be no more than one copy.

Variables

The same method easily supports mutable variables, leaving const :

 inline std::string player_name = "<subject name here>"; 

This is a global variable with external communication. Since its variable, most of the advantages of Iv mentioned above by Pete answer are gone, but some (for example, only declare the variable in one place and do not need any references) are still present. However, they may not cost a little extra compilation time and lack of compatibility with C ++ 14.


¹ For a const or constexpr compiler / optimizer can completely exclude this variable if it is not needed. Theoretically, he may decide to copy it into immediate value or something else; in practice, you probably should not worry about this, because it will only do this if it has a good reason, and this should make the final executable smaller and / or faster. You can probably configure this with -Os instead of -O3 .

² Every object file that used a constant would still have a copy, but they would be merged during the link. The only way to avoid this is with extern variables.

³ This simplified example works even without inline or with an array only const instead of constexpr , but they are useful for more complex real-world situations.

+4
source share

In the old days, we declared a constant in the header file and defined it in the source file:

 // constants.h extern const int size; // constants.cpp #include "constants.h" const int size = 3; // usage std::cout << size << '\n'; 

But maybe it is too simple; why not do it with a 10-line template and funky instance syntax?

+5
source share

I suggest using a template shell that allows you to define a global constant of any type in the header file and generate only one copy through all translation units (C ++ 1z is not required):

 template<typename TDummy = void> class t_GlobalValueHolder final { public: using t_Value = const int; // can be anything public: static t_Value s_value; }; template<typename TDummy> typename t_GlobalValueHolder<TDummy>::t_Value t_GlobalValueHolder<TDummy>::s_value{42}; // usage ::std::cout << t_GlobalValueHolder<>::s_value; 

Please note: usually access to s_value should be limited to only a subset of classes / methods by protecting the value and getting the user class from t_GlobalValueHolder or simply listing all the relevant elements as friends of t_GlobalValueHolder . And proper access control is another advantage of this method over "extern const", in addition to eliminating the need to have a translation block containing a definition of the value.

+1
source share

All Articles