Multiple aspects of Cpp static

Some notes about the static keyword. Its basic semantiocs, and concepts regarding declaration, defiantion and initilization. And also how the header only library use the static keyword.

Within one translation unit

In the “Complete C++ tips & secrets for professionals” The keyword static belongs to the type of storage class specifiers, it describes three semantics for using the static, before diving to details, let’s go through some basic concepts:

Translation unit: refer to this. Generally speaking, it is the file that can be compiled into the object file (such as .c or .cpp file). Which is the basic compiling unit of the cpp.

The fundamental idea to explain the static is that: the static in cpp means that the variabled is initilized once before the program starts (not the runtime), and is not destroyed every time its scope is exited.

Be careful about the term “initilized once”.

Here are some explanations about the declaration, defination and initilization. The defination can be written in the same line with the initilization. Consider following two exmaples:

//Case 1
for (int i = 0; i < 10; i++)
{
static std::string filename = "test" + std::to_string(i);
std::cout << "index " << i << " " << filename << std::endl;
}

and

//Case 2
static std::string filename;
for (int i = 0; i < 10; i++)
{
filename = "test" + std::to_string(i);
std::cout << "index " << i << " " << filename << std::endl;
}

The case 1 does not work as expected and all results are test0, since at the second time, the initlization operation will not be executed. At the case 2, the value of the static variable still can not be changed. The resutls works as expected. Attention, the static varaible does not say that its value can not be changed or reassigned, it just say that the associated value is initilzied once.

Across multiple translation units

The false impression about that the “static variable can not change its value” may come from the following use case, which use the static varibale across the translation uint like this:

//main.cpp
#include <iostream>
#include "test.hpp"
int main()
{

std::cout << a << std::endl;
change(10);
std::cout << a << std::endl;

return 0;
}

//test.hpp
static int a;
void change(int);

//test.cpp
#include "./test.hpp"

void change(int v){
a=v;
return;
}

The important thing is that there are two tranlation unit (main.cpp and file.cpp), these two unit have the different version of the static variable. Although one of them (the one in the file.cpp) is changed by the function, the one in the main.cpp is still not modified.

It’s interesting that it is actually a bad practice to define the variable in the .h file, if we remove the static keyword, we get the compiling error such as:

duplicate symbol '_a' in:
/var/folders/qy/xfjhf6951m5cq40nnrlyt30h0000gn/T/main-adcb06.o
/var/folders/qy/xfjhf6951m5cq40nnrlyt30h0000gn/T/test-6cb44b.o

The static keyword just hidden this issue! (The static is also unnecessary here)

The good practice is using the declaration in the .h file, such as extern int a and put the actuall defination and initilization at the .cpp file, which can avoid the multiple symbol compiling issue.

Header only libraries

This is a good question. In some situations, we want to build the header only library without including the .cpp file as the separate translation units. By this way, we do not need extra compiling process for building the library that we need. This is common for the utility function such as the timer and the logging.

The main issue here is how to avoid breaking the ODR (one defination rule). For example, our utility.h file might be included by multiple files. In this case, we might include multiple headers (with function defination and variable defination in different translation units). That is why we use the extra .cpp file as the separate translation unit.

For the multiple definations of the function, we can use inline keyword to solve it, by declaring it as inline keyword, the compiler allows the multiple defination in different translation unit. In fact, the function implemented in the class file is declared as the inline function implicitly.

For the multiple definations of the global variabe, just use the static variable as the global function, instead of getting is direactly, we can use a specific getter function to access associated global variable (return the reference of the global variable). This answer shows both the cpp version and c version.

References

https://pabloariasal.github.io/2020/01/02/static-variable-initialization/

This is insightful one
https://stackoverflow.com/questions/16079235/static-variable-cpp-do-not-want-to-change

defination, declaration initilization
https://stackoverflow.com/questions/23345554/what-distinguishes-the-declaration-the-definition-and-the-initialization-of-a-v

How to process global variable in header only library
https://stackoverflow.com/questions/51612866/global-variables-in-header-only-library

https://stackoverflow.com/questions/64388682/inline-functions-across-multiple-c-translation-units

推荐文章