使用模板解决多个定义

Dar*_*ieb 6 c++ templates compilation c++14

a.hpp:

#pragma once

struct S
{
  static int v;
};
int S::v = 0;
Run Code Online (Sandbox Code Playgroud)

b.hpp:

#pragma once

void addOne();
Run Code Online (Sandbox Code Playgroud)

b.cpp:

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

void addOne()
{
  S::v += 1;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

使用g++ -std=c++14 main.cpp b.cpp && ./a.out(S :: v的多个定义)进行编译时不起作用.

但是,当我将代码更改为:a.hpp时:

#pragma once

struct S
{
  template<typename T>
  static int v;
};
template<typename T>
int S::v = 0;
Run Code Online (Sandbox Code Playgroud)

S::vS::v<void>它来替换所有编译并运行我想要的第一个例子工作(输出5).

我相信我知道为什么第一个代码示例不起作用:该int S::v = 0;行在main.cpp单元中编译一次,在b.cpp单元中编译一次.当链接器将这两者链接在一起时,变量S::v基本上被重新定义.(?)

为什么带模板的代码有效?

Rei*_*ica 4

为什么带有模板的代码可以工作?

本质上,因为标准是这么说的。

对于模板,规则通常相当于:“使用它们的每个人都必须有可用的定义。” 这同样适用于类模板的静态数据成员:此类静态数据成员的定义必须存在于使用 odr 的每个翻译单元中。由编译器和链接器来确保这不会导致错误。

请注意,从 C++17 开始,您可以通过使静态数据成员内联来解决非模板情况:

#pragma once

struct S
{
  static inline int v = 0;
};
Run Code Online (Sandbox Code Playgroud)