【发布时间】:2019-04-30 18:41:15
【问题描述】:
在 C++14 中声明一个引用模板而不初始化主引用模板是否合法,只要它从未实例化?
template<class T>
const T& ref;
template<>
auto ref<int> = 1;
auto x = ref<int>;
这会在 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.
必须初始化主引用模板是没有意义的,因为在它被实例化之前,它是一个模板,而不是一个引用。
我发现我可以这样做:
template<class T>
const T& ref = "Meaningless initialization with any value of any type";
template<>
auto ref<int> = 1;
auto x = ref<int>;
因为显然 GCC 和 Clang 都接受但忽略引用模板初始化程序 RHS,只要它是一个有效的表达式并且主引用模板永远不会被实例化。并且任何类型的任何表达式都满足 Clang 的初始化要求。
只要主引用模板从未实例化,GCC 就不需要初始化器。这似乎是“精神上”的正确行为,因为在实际实例化引用模板之前,它不应该需要初始化程序。
标准在参考模板上并不是 100% 明确的。以下是我在变量模板实例化方面能找到的一点点:
14.7.1
除非变量模板特化已被显式实例化或显式特化,否则变量模板特化会在使用特化时隐式实例化。
...
实现不应隐式实例化...一个不需要实例化的变量模板...。
14.7.2
除了内联函数、类型从其初始值设定项或返回值 (7.1.6.4)、
const文字类型变量、引用类型变量和类模板特化、显式的声明实例化声明具有抑制它们引用的实体的隐式实例化的效果。 [注意:目的是作为显式实例化声明主题的内联函数在使用odr(3.2)时仍将被隐式实例化,以便可以考虑将主体内联,但没有外联副本内联函数的一部分将在翻译单元中生成。—尾注]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
那么,问题是为什么参考模板应该有所不同?为什么引用模板的主要声明必须是具有引用初始化的定义,而其他任何模板都不是这样?
template<class T> const T& ref; // Declaration, not definition
template<> const int& ref<int> = 1; // Specialization definition
const int& iref = ref<int>; // Specialization instantiation
【问题讨论】:
-
一个变量的声明就是一个定义,除非它是用
extern关键字声明的。如果你的意思是声明,你可以表达它:template <class T> extern const T& ref;。 extern 不会改变链接。 -
这是一个变量模板的声明,而不是一个变量。
-
评论可能更迂腐、更笼统、更难理解:basic.def
标签: c++ templates language-lawyer clang++ variable-templates