How to use extern to refer to an external variable with the same name as variable declared statically?

In the function increment() below, how do I refer to sameName from file2.cpp?

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>

using std::cout;
using std::string;

int main()
{
    extern int sameName; // sameName from file2.cpp
    cout << "main: " << sameName << "\n";
    {
        extern void increment();
        increment();
    }
    cout << "main (after increment): " << sameName << "\n";

    return 0;
}


file1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

using std::cout;

static int sameName = 200;

void increment()
{
    extern int sameName;  // WANT: We want to refer to sameName from file2.cpp in this scope.
    sameName++; // increment sameName from file2.cpp
    cout << "increment: " << sameName << "\n";
}


file2.cpp
1
2
3
4
5
6
7
8
#include <iostream>
#include <string>

using std::cout;
using std::to_string;
using std::string;

int sameName = 300;


Current output is:
1
2
3
main: 300
increment: 201
main (after increment): 300
This violates ODR (UB, no diagnostic is required).

One and only one definition of every non-inline function or variable that is odr-used is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.
https://en.cppreference.com/w/cpp/language/definition#One_Definition_Rule
I was going to suggest OP put their static variable in an unnamed namespace, but I found some divergence:
a.cpp
1
2
int n = 10; 
int& rn_a = n; // reference to n from a.cpp 

b.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
extern int& rn_a;

#define PRINT std::cout << "n (from a.cpp) = " << rn_a << "; n (from b.cpp) = " << rn_b << '\n'

namespace { 
  int n = 20; 
  int& rn_b = n; 
}

int main()
{
  n++;    PRINT; 
  ::n++;  PRINT;

  extern int n;
  n++;    PRINT;
  ::n++;  PRINT;
}

I don't understand what is happening with this program. I think both ns are different variables (so there is no ODR violation) because of [namespace.unnamed]:
An unnamed-namespace-definition behaves as if it were replaced by
1
2
3
inlineopt namespace unique { /* empty body */ }
using namespace unique;
namespace unique { namespace-body } 

https://eel.is/c++draft/namespace.unnamed

MSVC yields this output:
n (from a.cpp) = 10; n (from b.cpp) = 21
n (from a.cpp) = 10; n (from b.cpp) = 22
n (from a.cpp) = 10; n (from b.cpp) = 23
n (from a.cpp) = 10; n (from b.cpp) = 24

But GCC and Clang produce:
n (from a.cpp) = 10; n (from b.cpp) = 21
n (from a.cpp) = 10; n (from b.cpp) = 22
n (from a.cpp) = 11; n (from b.cpp) = 22
n (from a.cpp) = 11; n (from b.cpp) = 23

I tried several linkers with Clang and GCC: ld, lld, and gold. No change.

I suspect there is a name lookup bug. I'm not sure which behavior is correct. MSVC's compiler flag /permissive- affects name lookup but didn't make any difference here.
Last edited on
JLBorges wrote:
This violates ODR


I see. I just wanted to understand the limitations of extern. Apparently, extern can't be used to declare a variable that exists in another compilation unit if it would cause a naming conflict .

The example I gave is probably flawed. If I wanted a function from file1.cpp to modify a variable from file2.cpp, the variable being modified should have scope (e.g., contained in a class) or, if in global scope, be named so as to avoid name clash.
Topic archived. No new replies allowed.