c++staticlinkagestorage-durationtranslation-unit

How can static local variable shared along different translation unit?


How can static local variable shared along different translation unit?

I know that "static" specifies internal linkage.

In case of "static" member function having same address in different translation units, each translation unit has a copy of the function, and linker picks one of the copies to be included in executable.

Then, in the scenario of two different translation includes 1 shared header, which has a class with static member function that increments static local variable, and each translation unit (cpp) calls the the static member function of the shared header, each translation unit will increment its own independent static local variable because static specifies internal linkage. For example,

Shared.h

#pragma once

class CShared
{
public:

    static inline void Foo()
    {
        static int s_nCount = 0;
        ++s_nCount;
    }

private:

    CShared(){}
    ~CShared(){}
};

A.h

#pragma once
class A
{
public:

    A(){}
    ~A(){}

    void Foo();
};

A.cpp

#include "A.h"
#include "Shared.h"

void A::Foo()
{
    CShared::Foo();
}

B.h

#pragma once
class B
{
public:

    B(){}
    ~B(){}

    void Foo();
};

B.cpp

#include "B.h"
#include "Shared.h"

void B::Foo()
{
    CShared::Foo();
}

main.cpp

#include <iostream>
#include "A.h"
#include "B.h"

int main()
{
    A a;
    B b;

    a.Foo();
    b.Foo();

    return 0;
}

The result is s_nCount becomes 2 when b.Foo(). Therefore, the static local variable is "shared", not each translation unit having its own copy of static local variable, along different translation unit. Doesn't this mean the static local variable is not internal linkage? Isn't "shared between different translation unit" and "internal linkage" is conflicting term?

Anyone who clarifies this will be truly appreciated.

[Update]

So far it seems,

static linkage
CShared::Foo something other than internal linkage
s_nCount external linkage (Mike Nakis, The Dreams Wind) or no linkage (Vlad from Moscow)

According to Some programmer dude,

static linkage
CShared::Foo no linkage (cppreference)
s_nCount no linkage (cppreference)

In my understanding, CShared::Foo belongs to "no linkage - local classes and their member functions" s_nCount belongs to "no linkage - variables that aren't explicitly declared extern (regardless of the static modifier)"

Can anyone conclude this?

[Update2]

static linkage
CShared::Foo external linkage
s_nCount no linkage

About "static" meaning in front of member function

"static - static or thread storage duration and internal linkage (or external linkage for static class members not in an anonymous namespace)."

"Static class members have external linkage. Class member functions have external linkage."

About "static" meaning in front of "global" variable

About "static" meaning in front of "local" variable


Solution

  • In this member function declaration

    static inline void Foo()
    {
        static int s_nCount = 0;
        ++s_nCount;
    }
    

    the local variable s_nCount does not have internal linkage. It is not a namespace variable. It is a local variable declared without the storage class specifier extern. It is a local variable that has static storage duration and no linkage. So the function has only one its local variable that is initialized only once and is changed in each call of the function.

    From the C++17 STandard (6.5 Program and linkage)

    3 A name having namespace scope (6.3.6) has internal linkage if it is the name of...

    8 Names not covered by these rules have no linkage. Moreover, except as noted, a name declared at block scope (6.3.3) has no linkage.

    and (10.1.6 The inline specifier)

    1. .... [ Note: A static local variable in an inline function with external linkage always refers to the same object. A type defined within the body of an inline function with external linkage is the same type in every translation unit. — end note ]

    Here is an example of a variable declared in block scope that has internal linkage.

    #include <iostream>
    
    static int n;
    
    void f()
    {
        extern int n;
    
        ++n;
    }
    
    int main()
    {
        for ( size_t i = 0; i < 5; i++ )
        {
            std::cout << ( f(), n ) << ' ';
        }
    
        std::cout << '\n';
    }
    

    The program output is

    1 2 3 4 5
    

    So the variable n declared in the global namespace with the storage class specifier static has internal linkage. The local variable n declared in block scope of the function f denotes (refers to) the variable n with internal linkage declared in the global namespace.