Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple definition gets solved with templates

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?

like image 885
Darius Duesentrieb Avatar asked May 07 '18 11:05

Darius Duesentrieb


1 Answers

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;
};
like image 146
Angew is no longer proud of SO Avatar answered Nov 16 '22 15:11

Angew is no longer proud of SO