Multiple aspects of Cpp static and const

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

Within one translation unit (static)

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, once the variable is defined, that memory storage associated with that variable just stay there.

Be careful about the term “initilized once”.

Here are some explanations ideas about the declaration (signature), defination (contains the body of function or class) and initilization (declaraion + assignment). 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);
//the above line contains defination, declaration and initilization in one line, we try to redefine it each time
//it will not be redefined, and the memory space is always same for that variable
std::cout << "index " << i << " " << filename << std::endl;
}

and

//Case 2
static std::string filename;
//the above line contains both defination and declaration
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 it can not be redefined.

According to this blog this initilization is actually the combination of one declaration and one assignment. For static keyword, the memory space is fixed after the declaration of a variable, then after the assignment operation, the value is fixed. If you redefine that variable, the memory space is still there. This is different with the const keyword, the const keyword just limit the accessibility.

Const member function

When labeling a function as the const function, it means that this function is a “read-only” function that doesn’t modify the object for which it’s called, refer to this.

It is easy to trigger the error such as “passing ‘const …’ as ‘this’ argument discards qualifiers”. For example, when we have an non-const instance of class, we can call the const member function on this object and we can also call non-const function for this object. However, if we have a const instance of a class, we can only call the const member function for this class. Basically, the const object can only call the const function, otherwise, we will see above error about discarding qualifiers.

Const variable

The const variable is simple to understand, the variable with the const key word is a read only variable, and can not be modified.

One trick is that if the parameter is a reference to a variable, and it is not labeled as a const parameter, we can not assign a rvalue to it. The detailed reason can be found here. Namely, a temporary can not bind to a non-const reference. This is because that modifying a temporary is meaningless, its scope is short, it is destroyed after the expression.

Different between const and static

It might good to discuss a little bit about the difference between the const and static here. The good way to understand these two keyword is to consider its opposite word. For const, its opposite word is no-const. So the value of const variable can not be changed, or it can not be reassigned. Or we can assume it is read-only variable. But it can be redefined, which is different with the static keyword.

int main() {
for(int i=0;i<2;i++){
const int a = i;
std::cout << a << std::endl;
}

std::cout << "------" << std::endl;

for (int i = 0; i < 2; i++)
{
static int b = i;
//the above line contains defination, declaration and initilization in one line, we try to redefine it each time
std::cout << "index " << b << std::endl;
}
return 0;
}

//output
0
1
------
index 0
index 0

In the above example, the value of static variable does not change if we try to redefine it. If we try to change the value of const variable, such as reassign another value to a, there is error error: assignment of read-only variable 'a'.

Across multiple translation units (static)

I have an incorrect understanding previously: the “static variable can not change its value”. This understanding may come from the following use case, which uses 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 and global variable

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.

Global variable for library

Initilizing the variable as static element in the cpp file might be a good option to do for the use case such as creating a logging library or tracing library which has a global scope. This is one example we adopted in the program to declare a global tracing class and link it from different other classes. The good thing is that we can declare the wrapper to the inner buffer object as needed in different classes and there is one version of the buffer in memory since it is declared as a static variable.

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

推荐文章