Search code examples
c++c++17inlinelinkage

const vs non-const inline variables


Suppose I have the following two files:

test.cpp

inline double pi { 3.1415 };
#include <iostream>

void test() {

    std::cout << pi << std::endl;

}

and

main.cpp

inline double pi { 2.178 };

void test();

#include <iostream>

int main() {

    std::cout << pi << std::endl;
    test();

}

inline variables by default have external linkage. So, my initial thought, according to the One Definition Rule (ODR) is that the same variable can be declared across multiple translation units as long as the variable is declared inline and contains the same exact definition

So my first question is, why does this program compile if, even though the variables are declared inline, they have different definitions across the translation units?

Second, what is the difference between an inline non-const and an inline const variable?

For example, if you run the code above you should get the following output

2.178
2.178

However, if you go to both files and make pi a const double, that is inline const double pi {3.1415} and inline const double pi {2.178}

You get the following output:

2.178
3.1415

What exactly is happening here?


Solution

  • Your program has undefined behaviour, for exactly the reason you give.

    Your toolchain is not required to diagnose undefined behaviour and, in cases that are hard to "spot" in general, which is many of them, it won't even bother. At best, the linker (not the compiler) could spot this particular case during build, if the people who wrote it had deemed this to be useful enough to warrant the extra work and build time.

    But, no: this rule falls into the category of being the programmer's responsibility. You broke the rules, and now your program is broken, in complex ways that we could probably work out with enough information about your computer, toolchain, moon-phase, mood, shoe colour and program's assembly… but this would not really be of any value.

    Adding const may lead to the compiler (not the linker) eliding "references" to this variable in some places, literally "inlining" the initialiser value at the point of use, as an optimisation. It can do that when it knows the value won't have changed from initialisation (thanks to the const). The result is that you're no longer observing so much weirdness from your undefined behaviour, because the behaviour you see from your program is more constrained to the individual translation units.