6

a.hpp:

#pragma once

struct S
{
  static int v;
};
int S::v = 0;

b.hpp:

#pragma once

void addOne();

b.cpp:

#include "b.hpp"
#include "a.hpp"

void addOne()
{
  S::v += 1;
}

main.cpp:

#include <iostream>

#include "a.hpp"
#include "b.hpp"

int main()
{
  S::v = 2;
  addOne();
  S::v += 2;
  std::cout << S::v << std::endl;
}

Does not work when compiling with g++ -std=c++14 main.cpp b.cpp && ./a.out (multiple definition of S::v).

However when I change the code to: a.hpp:

#pragma once

struct S
{
  template<typename T>
  static int v;
};
template<typename T>
int S::v = 0;

and replace all S::v with S::v<void> it compiles and works how I intended the first example to work (outputs 5).

I believe I know why the first code example does not work: The int S::v = 0; line gets once compiled in the main.cpp unit and once in the b.cpp unit. When the linker links these two together, then the variable S::v gets essentially redefined.(?)

Why does the code with the template work?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Darius Duesentrieb
  • 837
  • 10
  • 20

1 Answers1

4

Why does the code with the template work?

Essentially, because the standard says so.

With templates, the rules usually amoun to: "everyone using them must have their definition available." The exact same applies to static data members of class templates: a definition of such a static data member must be present in every translation unit in which it is odr-used. It's up to the compiler & linker to make sure this does not lead to errors.

Note that since C++17, you can solve the non-template case by making the static data member inline:

#pragma once

struct S
{
  static inline int v = 0;
};
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • This answer is very simplistic. Explicit specializations work more like non-templates as far as linkage is concerned, for example, and the variable definition here is only required if the template has not been instantiated with the same parameters in another TU. Also, implementation details like strong vs. weak symbols might be interesting. I admit I'm too lazy to write a different answer right now… – Arne Vogel May 09 '18 at 12:45