【问题标题】:Should I declare my function template specializations or is defining them enough?我应该声明我的函数模板特化还是足够定义它们?
【发布时间】:2020-03-02 19:17:17
【问题描述】:

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

// 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。

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

【问题讨论】:

  • 你的主要是如何得到Class1和2的定义的?
  • 我实际上在单独的源文件中定义了我的类。我会更新代码;我希望这不会让它太混乱。
  • 从技术上讲,该程序是 UB,它违反了ODR。实际上,我无法想到它何时会成功编译和链接..
  • 为什么不使用常规重载?
  • 为什么要使用明显未实现的函数模板的显式模板特化而不是重载?

标签: c++ undefined-behavior template-specialization function-templates translation-unit


【解决方案1】:

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

[temp.expl.spec]

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

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

另一方面,有这样一段:

[温度]

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

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

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

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

【讨论】:

  • “我不能说这是故意还是遗漏。” 看起来是故意的。 wg21.link/cwg2138
【解决方案2】:

您必须在使用前声明每个显式特化。但是你可以在标头中声明它专门针对的类型。

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

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

【讨论】:

    猜你喜欢
    • 2020-09-28
    • 2021-03-24
    • 1970-01-01
    • 2021-10-23
    • 2022-10-14
    • 1970-01-01
    • 2014-12-07
    • 2019-09-23
    • 2018-11-03
    相关资源
    最近更新 更多