header 中声明的变量模板是否违反 ODR?

Are variable templates declared in a header, an ODR violation?

当 header 文件包含如下模板变量时会发生什么:

template <class T>
std::map<T, std::string> errorCodes = /* some initialization logic */;

这个变量可以安全使用吗?对此做了一些研究,我发现:

  1. 模板是隐式外部的,但这确实会导致 ODR 违规
  2. 如果没有任何 static 或 inilne 或 constexpr 修饰,我最终会得到多个定义
  3. C++17 的关键字 inline 改变了规则,但 c++20 有不同的行为(?)

那么是哪一个?

Is this variable safe to use?

是的,您可以在 headers 中以这种方式声明变量模板,然后在其他 翻译单元 中使用它们。举例如下:

header.hpp

#ifndef HEADER_H
#define HEADER_H
#include <map>
template <class T>
std::map<T, std::string> errorCodes{}; //zero initialized value. Also, note there is no need to use keyword inline here 

#endif

print.hpp

#ifndef PRINT_H
#define PRINT_H

void print();

#endif 

print.cpp

#include "print.hpp"
#include "header.hpp"
#include <iostream>
void print()
{
    std::cout << errorCodes<int>.at(15) << std::endl; // This is safe: prints SomeString
}

main.cpp


#include <iostream>
#include "print.hpp"
#include "header.hpp"
int main()
{
    errorCodes<int>[15] = "SomeString"; //This is safe 
    print();
}

请注意,在上面的程序中,您 不需要 在 header.hpp 中使用关键字 inline,同时定义 变量模板 errorCodes。 C++ 编译系统 会解决这个问题。

上面程序的输出可见here.

模板从 one-definition 规则中获得例外,[basic.def.odr]/13:

There can be more than one definition of a [...] templated entity ([temp.pre]) [...] in a program provided that each definition appears in a different translation unit and the definitions satisfy the following requirements.

那里有很多要求,但基本上如果你在 header 中有一个变量模板(变量模板是一种模板化实体)并且只包含来自多个翻译的 header单位,它们将具有相同的 token-for-token 相同的定义(因为 #include),因此可以有多个定义。

这和在header中不需要声明函数模板inline,但是do需要声明正则函数是一样的道理inline.


在 C++17 中,这种写法 read:

There can be more than one definition of a class type, enumeration type, inline function with external linkage ([dcl.inline]), inline variable with external linkage ([dcl.inline]), class template, non-static function template, static data member of a class template, member function of a class template, or template specialization for which some template parameters are not specified ([temp.spec], [temp.class.spec]) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements.

请注意,变量模板不在该列表中,这只是一个遗漏(它一直都是为了工作)。这是 CWG 2433,于 2019 年通过,但作为缺陷报告 (DR),因此我不会将其视为 C++20 更改。这是引入我之前引用的“模板化实体”项目符号的 DR(而不是手动列出几种不同类型的模板化实体,而遗漏了一种)。