是否需要初始化参考变量主模板,即使它从未实例化过?

lee*_*eek 12 c++ templates language-lawyer clang++ variable-templates

在没有初始化主要参考模板的情况下在C++ 14中声明参考模板是否合法,只要它从未实例化?

template<class T>
const T& ref;

template<>
auto ref<int> = 1;

auto x = ref<int>;
Run Code Online (Sandbox Code Playgroud)

这会在GCC和Clang上产生不同的结果:

$ g++ -std=c++14 -c ref.cpp
$

$ clang -std=c++14 -c ref.cpp
ref.cpp:2:10: error: declaration of reference variable 'ref' requires an
      initializer
const T& ref;
         ^~~
1 error generated.
Run Code Online (Sandbox Code Playgroud)

没有必要初始化主要参考模板,因为在实例化之前,它是模板,而不是参考.

我发现我可以这样做:

template<class T>
const T& ref = "Meaningless initialization with any value of any type";

template<>
auto ref<int> = 1;

auto x = ref<int>;
Run Code Online (Sandbox Code Playgroud)

因为显然GCC和Clang都接受但忽略了参考模板初始化器RHS,只要它是一个有效的表达式并且主参考模板永远不会被实例化.任何类型的任何表达式都满足Clang的初始化要求.

只要主参考模板从未实例化,GCC就不需要初始化程序.这似乎是"在精神上"的正确行为,因为在实际实例化参考模板之前,它不应该需要初始化器.

标准在参考模板上不是100%明确.这是我在变量模板实例化上找不到的东西:

14.7.1

除非已显式实例化或显式专门化变量模板特化,否则在使用特化时隐式实例化变量模板特化.

...

实现不应隐式实例化......不需要实例化的变量模板.

14.7.2

除了内联函数,从其初始值或返回值(7.1.6.4)推导出的类型的声明const,文字类型的变量,引用类型的变量和类模板特化,显式实例化声明具有抑制实体的隐式实例化的效果他们所指的.[注意:意图是当使用odr(3.2)时,仍然会隐式实例化作为显式实例化声明主题的内联函数,以便可以考虑使用内联体,但是不需要外联副本内联函数的内容将在翻译单元中生成.-end note]

14.7.3

明确专门化的函数模板,类模板或变量模板的声明应在声明显式特化之前.[ 注意:需要声明,但不需要模板的定义.- 注意 ].

编辑添加:

变量模板声明,类模板声明或函数模板声明分别与变量声明,类声明或函数声明不同,并且不受相同规则的约束.在模板实例化之前,它只是一个模板.

可以在不提供主要定义的情况下声明类模板,变量模板和函数模板,仅提供特化定义.以下代码对Clang和GCC都是合法的:

// Class
template<class T> class foo;        // Declaration, not definition
template<> class foo<int> {};       // Specialization definition
using ifoo = foo<int>;              // Specialization instantiation

// Function
template<class T> void bar(T);      // Declaration, not definition
template<> void bar(int) {}         // Specialization definition
void (*barp)(int) = bar<int>;       // Specialization instantiation

// Variable
int j;
template<class T> T* point;         // Declaration, not definition
template<> int* point<int> = &j;    // Specialization definition
int *k = point<int>;                // Specialization instantiation
Run Code Online (Sandbox Code Playgroud)

那么,问题是为什么参考模板应该有什么不同?为什么参考模板的主要声明必须是带有引用初始化的定义,而对于任何其他模板都不是这样?

template<class T> const T& ref;      // Declaration, not definition
template<> const int& ref<int> = 1;  // Specialization definition
const int& iref = ref<int>;          // Specialization instantiation
Run Code Online (Sandbox Code Playgroud)

Bri*_*ian 8

我相信这是[temp.res]/8所涵盖的:

......如果出现以下情况,该计划格式错误,无需诊断:

  • 没有为模板或模板中的constexpr if语句的子语句生成有效的特化,并且模板未实例化...

您编写的引用模板永远不会产生有效的特化,因为实例化生成的变量将始终需要初始化器.


我提供的引文来自C++ 17,但在C++ 14中有类似的陈述.

  • @leek不,你错误地解释了它所说的"不能为模板生成有效的专业化"的标准.这意味着实例化模板*所产生的特殊化*无效.显式专业化不计算在内. (3认同)