How can static local variable shared along different translation unit?

607 views Asked by At

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

3

There are 3 answers

16
Vlad from Moscow On BEST ANSWER

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.

4
Mike Nakis On

I think you might be confusing static in global scope vs. static within a class.

As a matter of fact, the wikibooks.org page that you linked to says so. I quote:

This results in the "static" keyword being used only in implementation files, never in header files, except when "static" is used inside a class definition inside a header file, where it indicates something other than internal linkage.

So, if you just have this in the global scope: static int a = 0; then a will have internal linkage.

But if you have class A { static int a = 0; } then a is simply a class variable, (that is, not an instance variable,) and it has external linkage.

1
The Dreams Wind On

static keyword can have different meaning when applied in different contexts. static keyword next to local variables introduces static storage duration, but not internal linkage (like the keyword does for global variables).

As long as the function where local static variable is declared has external linkage (which a static member function is), all translation units have the same static local variable shared. If the function had internal linkage all translation units would have their own instance of the static local variable (e.g. if you had it defined outside of the class declaration):

#pragma once

// Defined in the same header outside of the class body
static void Foo()
{
    static int s_nCount = 0;
    ++s_nCount;
}

class CShared
{
// ..Class definition goes here.. //
}
....