定义一个全局变量模板?

问题描述

通常,我们会在头文件中声明但不定义全局变量。但是,我们在其中定义了模板。那么问题来了:是否可以定义一个全局变量模板?

template <uint8_t PinCode>
uint8_t BitMask = digitalPinToBitMask(PinCode);

实际上是一个全局变量模板:

  • 仅在至少一个编译单元 (CU) 需要时才实例化。
  • 如果多个 CU 需要同一个实例,则不会导致重新定义错误
  • 在 CU 之间共享,即如果一个 CU 更改了一个实例的值,其他需要相同实例的 CU 将受到影响。
  • 对于每种必需的实例只调用一次初始化程序。

解决方法

该标准定义了许多模板化的全局常量,例如类型特征 type_trait_v 常量。

它们在标题中是这样定义的

template<class T>
inline constexpr some_type some_variable = ...;

在你的例子中,你可以做类似的事情

template<uint8_t Pin>
inline constexpr uint8_t BitMask = digitalPinToMask(Pin);

要使其工作,则 digitalPinToMask 必须在 constexpr 上下文中可调用。

,

经过长时间的研究,我终于找到了最优雅的解决方案。 定义风格取决于是否要初始化变量。

如果变量不需要初始化,只需要一个头文件:

template <int TimerCode>
int TCCRB;

是的,它很简单,而且一定很简单。不要像我们通常为头文件中的变量添加“static”或“extern”关键字。它将通过编译并作为所有 CU 之间的全局变量模板工作。相同的实例将共享相同的实际变量,即一个 CU 的变化会影响其他 CU,只要它们具有相同的模板参数。

我们知道,如果在头文件中定义了一个没有必要关键字(如“static”或“extern”)的变量,如果头文件包含在多个 CU 中,则会导致重新定义错误。 “静态”告诉编译器为每个 CU 复制此变量作为单独的变量,因此一个 CU 中的更改不会影响另一个 CU 中的更改。相反,“extern”告诉编译器这里的这个变量只是一个声明。它仅在一个 CU 中定义和拥有,其他 CU 应该只保留对它的引用或符号。因此,一个 CU 的变化将影响其他 CU。

然而,变量模板既不是静态定义也不是外部声明。这是对编译器的一条特殊指令:查找对这个模板的所有引用情况,组合相同模板参数的那些情况,并为每个唯一实例自动生成一个定义!这次编译器负责避免重新定义:它扫描所有 CU,然后自己生成唯一的定义!

如果你不想给变量一个初始值,如果有太多可能的实例无法一一列出,这种自动定义非常方便。但是,如果您确实想要一个初始值,则必须自己定义它,并且您需要一个单独的 CU 来拥有这些变量以避免重新定义:

//In the header file:
template <int TimerCode>
extern int TCCRA;
//In the CPP file:
template <>
int TCCRA<1> = 2;
template <>
int TCCRA<2> = 5;
//Naturally you have to provide initial values for all possible instances …

对于这种情况,“extern”关键字是必需的,因为您在您专门提供的 CU 中明确定义了所有有效实例,其他 CU 必须引用此定义。编译器本身不应生成随机定义。这很像一个普通的全局变量定义,只是增加了一些模板语法。但是,用户只能使用您的 CPP 文件中提供的实例。这也很自然,因为只有已知实例才能提供初始值。

我在互联网上发现关于这个主题的教程很少。希望我的经历能帮助到更多人~