1

我有一些可以检查的课程。实现这一点的代码在头文件中声明了一个函数模板,并将其专门用于不同的源文件:

// 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可以检查的类时,我不需要触摸头文件,因为它定义了一个非常宽的接口。如果我忘记实现 的check函数Class3,链接器会用错误消息提醒我。

我的问题是:这种行为是有保证的,还是我的代码是靠运气工作的?我正在使用 Visual Studio。

如果我想特化我的函数模板,我不应该在头文件中声明我的所有特化吗?

4

2 回答 2

3

为了安全起见,我会添加这些声明(好吧,假设我不会因为任何原因而重载)。我认为法律对此并不太清楚。一方面,我们有

[temp.expl.spec]

6如果模板、成员模板或类模板的成员被显式特化,则该特化应在第一次使用该特化之前声明,该特化将导致发生隐式实例化,在每个翻译单元中使用该特化发生;不需要诊断。如果程序没有为显式特化提供定义,并且该特化的使用方式会导致发生隐式实例化,或者该成员是虚拟成员函数,则该程序是非良构的,不需要诊断。永远不会为已声明但未定义的显式特化生成隐式实例化。

如果我没看错的话,这意味着如果将明确的特化添加到main.cpp,那么它必须出现在 之前 main。因为那是可能发生隐式实例化的地方。该段落不会使您的代码完全消除格式错误的 NDR,因为用法和显式专业化出现在不同的 TU 中。但这确实引起了担忧。

另一方面,有这样一段:

[温度]

7函数模板、类模板的成员函数、变量模板或类模板的静态数据成员应在隐式实例化的每个翻译单元中定义,除非相应的特化在某个翻译单元中显式实例化;不需要诊断。

这允许我们在单独的看不见的 TU中显式实例化。但它没有为明确的专业化提供许可。这是故意的还是疏忽的,我不能说。

它起作用的原因可能是由于整个事情是如何实现的。当函数声明被隐式实例化时,它会产生一个符号,恰好与显式特化产生的符号相匹配。匹配符号意味着一个快乐的链接器,所以一切都可以构建和运行。

但从语言律师的角度来看,我认为我们可以将这里的行为称为未定义的遗漏。它是未定义的,仅仅是因为标准没有解决它。所以回到我的开场白,为了安全起见,我会添加它们,因为至少那时放置是由标准解决的。

于 2019-11-06T11:28:11.943 回答
0

您必须在使用之前声明每个显式特化。但是您可以在声明它专门针对的类型的标题中执行此操作。

// class2.h
struct Class2 {int price;};
template <class T>
bool check(const T& object);
template <>
bool check(const Class2& object)

(我仍然不明白为什么不能使用重载)。

于 2019-11-06T10:44:01.360 回答