5

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

YoonSeok OH
  • 647
  • 2
  • 7
  • 15
  • 1
    Like almost everything else in C++, the meaning of `static` depends on *context*. If you define a static variable in namespace scope (like a global variable) it will have internal linkage an not be exported from its translation unit. Static block-local variables (like variables inside functions) have a lifetime from the first execution of the block, until the end of the process, and is shared for *all* executions of the block. This is well-documented and should be taught by any decent book, tutorial or class. – Some programmer dude Apr 30 '23 at 10:43
  • `A.h` header is missing from your question. – Jason Apr 30 '23 at 10:44
  • @Some programmer dude Thanks for your comment. Do you mean "static block local variable", unlike "static variable in namespace scode", has external linkage? – YoonSeok OH Apr 30 '23 at 10:54
  • 1
    @Jason Thanks for your comment. I will add it to the question. – YoonSeok OH Apr 30 '23 at 10:54
  • 2
    @YoonSeokOH For local variables the issue of linkage does not arise. They are never seen by the linker. – john Apr 30 '23 at 11:15
  • [This storage duration and linkage reference](https://en.cppreference.com/w/cpp/language/storage_duration) might be helpful to read. – Some programmer dude Apr 30 '23 at 11:53
  • @Some programmer dude Thanks for your help. I wonder where it clearly states that static local variable is no linkage or external linkage. ["Function-local static objects in all definitions of the same inline function (which may be implicitly inline) all refer to the same object defined in one translation unit, as long as the function has external linkage."](https://en.cppreference.com/w/cpp/language/storage_duration). This was as far as I can find so far. – YoonSeok OH Apr 30 '23 at 12:02
  • 1
    https://en.cppreference.com/w/cpp/language/storage_duration#no_linkage "Any of the following names declared at **block scope** have **no linkage**: ... variables that aren't explicitly declared `extern` (**regardless of the `static` modifier**);" (emphasis mine) So it doesn't matter if a variable declared in a block have `static` or not, as long as it's not declared as `extern` it has no linkage. – Some programmer dude Apr 30 '23 at 12:08

3 Answers3

4

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.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • "_normal linkage_" would be _external linkage_. It is not obvious to me that one is more normal than the other. (Well, except that it is possible to give a class internal linkage as well.) – user17732522 Apr 30 '23 at 10:49
  • @user17732522 you are right, I corrected that. – Mike Nakis Apr 30 '23 at 10:52
  • @Mike Nakis Thanks for your answer. Oh. So static within class, such as static local variable in my example, is not internal linkage. In other words, it is external linkage. – YoonSeok OH Apr 30 '23 at 11:00
  • 1
    I just got to wonder what that " something other than internal linkage" means no linkage or external linkage. – YoonSeok OH Apr 30 '23 at 12:04
3

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Storage duration and linkage are different things. – Jason Apr 30 '23 at 10:46
  • @Jason Your comment is irrelevant relative to my answer, – Vlad from Moscow Apr 30 '23 at 10:52
  • @Vlad from Moscow Thanks for your answer. So, the static local variable "s_nCount" has external linkage, and it is just a local variable that has static storage duration. It is nice of you providing C++17 standard. – YoonSeok OH Apr 30 '23 at 11:08
  • @YoonSeokOH The local variable s_nCount has no linkage. – Vlad from Moscow Apr 30 '23 at 11:10
  • @Vlad from Moscow I just checked that ["variables that aren't explicitly declared extern (regardless of the static modifier)"](https://en.cppreference.com/w/cpp/language/storage_duration). If it is not external, could you please explain how s_nCount in B.o reflects modification from s_nCount in A.o? I don't get this how if they are not external linkage. – YoonSeok OH Apr 30 '23 at 11:24
  • @YoonSeokOH The variable s_nCount is a local variable of the static member function foo of the class CShared. It has static storage duration. It means that it is alive and keeps its value between function calls. – Vlad from Moscow Apr 30 '23 at 11:35
  • @Vlad from Moscow Yes s_nCount has static storage duration. Could you please explain in terms of linkage? A.cpp and B.cpp are two different translation unit. They both has copies of Shared.h. If s_nCount resides in each translation unit, b.Foo() would increment its own s_nCount from 0 to 1. However, the result shows increment from 1 to 2, which means s_nCount in B.cpp refers to same data that is being refered by A.cpp. So I thought this result means external linkage. Am I missing something? – YoonSeok OH Apr 30 '23 at 11:47
  • @YoonSeokOH The static inline function Foo has one the same static variable that is changed in each function call. – Vlad from Moscow Apr 30 '23 at 12:09
  • @YoonSeok OH See my answer. I appended it with one more quote from the C++ Standard. – Vlad from Moscow Apr 30 '23 at 12:33
  • Sorry I was editing summing up what I have understood so far in the question. – YoonSeok OH Apr 30 '23 at 12:34
  • @Vlad from Moscow Oh wait. "A static local variable in an inline function with external linkage..." This means CShared::Foo is external linkage. Am I wrong? – YoonSeok OH Apr 30 '23 at 12:36
  • @YoonSeokOH The resume/ The variable s_nCount has no linkage. But it has static storage duration. That is it is alive during the program execution and between calls of the function. The function Foo itself is an inline static member function of the class all implementations of which in different compilation units refer to the same its local variable s_nCount. – Vlad from Moscow Apr 30 '23 at 12:41
  • @Vlaf from Moscow I think you are right. I will add secondary update. Please correct me if I am wrong. – YoonSeok OH Apr 30 '23 at 12:50
  • I've been swimming around C++17 standard pdf and now I think I understood what you've said. CShared::Foo has external linkage (regardless of inline). static int s_nCount has no linkage, but storage duration is just static (which means it is not destroyed after block exit). – YoonSeok OH Apr 30 '23 at 19:50
  • It was not s_nCount that was "seen" from both cpps, but same CShared::Foo was "seen" from both cpps. s_nCount is just local variable having static storage duration when entering the block scope. This also explains why address of s_nCount and CShared::Foo is same from both cpps. – YoonSeok OH Apr 30 '23 at 19:56
  • @YoonSeokOH You are right. I wrote about that in my answer and comments.:) – Vlad from Moscow Apr 30 '23 at 19:56
3

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.. //
}
....
The Dreams Wind
  • 8,416
  • 2
  • 19
  • 49
  • Thanks for your answer. Now I got clarified that static local variable is not internal linkage, rather it just means storage duration. – YoonSeok OH Apr 30 '23 at 11:03