0

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

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

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

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

2 回答 2

1

该标准定义了许多模板化的全局常量,例如类型特征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上下文中可调用。

于 2021-07-16T14:01:40.663 回答
1

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

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

template <int TimerCode>
int TCCRB;

是的,它是而且必须如此简单。不要像我们通常对头文件中的变量那样添加“静态”或“外部”关键字。它将通过编译并作为所有 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 文件中提供的实例。这也很自然,因为只有已知实例才能提供初始值。

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

于 2021-07-17T08:11:17.407 回答