c++inline

Is each static library supposed to get its own copy of an inline variable?


It's supposed to be, according to info I've heard, that if you have a variable such as:

inline int a_var;

or

struct Foo
{
     static inline int a_var;
};

In a header file and include that header somewhere in a static library, library2, and then include that in a library, Library2, then each library will have its own copy of that variable.

So I tested this. I made an executable and linked to static Library 1 and static Library2.

I declare a variable in a struct in a common header:

struct MyStruct
{
    static inline int static_var = 0;
};

And in my executable/main() I do:

void library_one_function();
void library_two_function();

#include "common_header.h"
#include <iostream>

int main()
{
    MyStruct::static_var = 1;
    std::cout << "Value from executable = " << MyStruct::static_var << "\n"; // I GET 1
    library_one_func(); // LIBRARY ONE FUNCTION CHANGES IT TO 7
    std::cout << "Value from executable = " << MyStruct::static_var << "\n"; // I GET 7
    library_two_function(); // LIBRARY TWO FUNCTION CHANGES IT TO 100
    std::cout << "Value from executable = " << MyStruct::static_var << "\n"; // I GET 100

}

Every single time I reference that variable there's no indication that each library has its own copy. It's always the same. Why is it then that people say that inline variables get their own copies?


Solution

  • Is each static library supposed to get its own copy of an inline variable?

    C++ doesn't say.

    C++ does acknowledge the possibility of separate compilation and pre-compiled libraries, but it does not prescribe any details, and it does not allow for their use to affect the behavior or semantics of well-formed C++ programs. The involvement and characteristics of libraries is an implementation detail that is not relevant to C++ semantics.

    If, in a given implementation, multiple static libraries each got their own copies of static inline data members then it would be the responsibility of that implementation's linker to do whatever is needed to ensure that the C++ specification that ...

    If a static data member is not declared thread_local there is one copy of the data member that is shared by all the objects of the class.

    (class.static.data; C23 11.4.9.3/1)

    ... is is satisfied. That could be by merging multiple copies into one in some appropriate way, by refusing to link at all if there was a conflict, or perhaps by some other means.

    Why is it then that people say that inline variables get their own copies?

    Any well-informed people saying that and intending to be interpreted as you are doing, that such duplication could be visible within a C++ program, are remarking on possible avenues to implementation non-conformance. That is unlikely to be observed with typical implementations of static libraries. There is perhaps more risk with some implementations of shared libraries, but that is, again, an implementation detail.


    Additional note

    In comments, you quoted some remarks you attributed to Raymond Chen, regarding a different situation, about which you said:

    The topic was whether when you have getID() { static int counter = 0; return counter++;} defined in a header would result in each static library having its own counter variable. I just tried this version with the counter and they all used the exact same copy. However I'm not sure if this is defined.

    That pertains to a static local variable of an inline function, not a static inline variable as in the examples in your question. In any case, although the spec may have been unclear about that at the time such an uncertainty was expressed, recent versions contain a note clarifying that (C++23 version):

    Note 3 : An inline function or variable with external or module linkage can be defined in multiple translation units (6.3), but is one entity with one address. A type or static variable defined in the body of such a function is therefore a single entity.

    (dcl.inline; C++23 9.2.8/6)

    Thus the required semantics are analogous: there is only one copy of the static local variable as far as C++ behavior and semantics are concerned, whether the function is defined inline or not, regardless of how many or what kind of libraries are involved.


    Additional additional note

    You can, of course, simply avoid static inline data members, and perhaps also static local variables of inline functions. Non-inline static data members (that are accessed) must have exactly one definition -- so not in a header -- and it is both necessary and relatively easy to ensure that that does not get duplicated.