我有一些类可以检查。实现这一点的代码在头文件中声明了一个函数模板,并将其专门用于不同的源文件:
// check.h
template <class T>
bool check(const T& object);
// class1.h
struct Class1 {int mass;};
// check_class1.cpp
#include "class1.h"
#include "check.h"
template <>
bool check(const Class1& object) {return object.mass < 100;}
// class2.h
struct Class2 {int price;};
// check_class2.cpp
#include "class2.h"
#include "check.h"
template <>
bool check(const Class2& object) {return object.price < 1000;}
// class3.h
struct Class3 {int x;};
... // 10 more classes which I can check
这个代码是这样使用的:
#include "class1.h"
#include "class2.h"
#include "class3.h"
#include "check.h"
int main()
{
Class1 object1{50};
Class2 object2{500};
Class3 object3{8};
check(object1); // OK
check(object2); // OK
check(object3); // a link error appears here
}
这个效果很好。当我添加另一个可以检查的类Class3
时,我不需要触摸头文件,因为它定义了一个非常宽的接口。如果我忘记为Class3
实现check
函数,链接器将用错误消息提醒我。
我的问题是:这种行为是有保证的,还是我的代码靠运气工作?我正在使用Visual Studio。
如果我想专门化我的函数模板,我不应该在头文件中声明我的所有专门化吗?
为了安全起见,我会添加这些声明(好吧,假设我不会因为任何原因而重载(。我认为法律对此不太明确。首先,我们有
[温度解释规范]
6如果模板、成员模板或类的成员模板是明确专门化的,那么专门化应该是在第一次使用将导致在发生这样的使用;不需要进行诊断。如果程序没有为明确的专业化提供定义,并且或者专用化的使用方式会导致要进行的隐式实例化或成员是虚拟成员功能,程序格式错误,无需诊断。一从不为显式生成隐式实例化已声明但未定义的专门化。
如果我读得正确,这意味着如果向main.cpp
添加了显式专用化,那么它必须出现在main
之前。因为这就是可能发生隐式实例化的地方。这段话并没有让你的代码变成格式错误的NDR,因为用法和明确的专业化出现在不同的TU中。但它确实引起了人们的担忧。
另一方面,有这样一段话:
[temp]
7一种函数模板、类模板的成员函数,类模板的变量模板或静态数据成员定义在每个隐含的翻译单元中实例化,除非显式指定相应的专门化在某个翻译单元中实例化;不需要进行诊断。
这个允许我们在单独的看不见的TU中显式地实例化。但它并没有为明确的专业化提供津贴。我不能说这是故意的还是疏忽。
它之所以有效,很可能是因为整个事情是如何实现的。当函数声明被隐式实例化时,它会生成一个恰好与显式专门化生成的符号匹配的符号。匹配的符号意味着一个愉快的链接器,所以一切都会构建和运行。
但从语言律师的角度来看,我认为我们可以将这里的行为称为不作为。它是未定义的,只是因为标准没有解决它。所以回到我的开场白,为了安全起见,我会添加它们,因为至少这样的位置是由标准解决的。
您必须在使用每个显式专用化之前声明它们。但是,您可以在声明其专用类型的标头中执行此操作。
// class2.h
struct Class2 {int price;};
template <class T>
bool check(const T& object);
template <>
bool check(const Class2& object)
(我仍然不明白为什么使用重载不是一种选择(。