class 模板的显式特化的定义应该放在 C++ 中的什么地方?

Where should the definition of an explicit specialization of a class template be placed in C++?

根据[temp.spec]/5

For a given template and a given set of template-arguments,

  • ...

  • an explicit specialization shall be defined at most once in a program (according to [basic.def.odr]), and

  • ...

class 模板的显式(完全)特化的定义不能放在 header 中(否则在包含此 header 的每个翻译单元中都有一个定义,这样整个程序就会有不止一个定义。

此外,作为另一个证据,[basic.def.odr]/12 中列出的实体(下面引用块)不包含 class 模板的完整专业化。相反,包含“未指定某些模板参数的模板专业化 ”。

There can be more than one definition of a class type, enumeration type, inline function with external linkage ([dcl.inline]), inline variable with external linkage ([dcl.inline]), class template, non-static function template, concept ([temp.concept]), static data member of a class template, member function of a class template, or template specialization for which some template parameters are not specified ([temp.spec], [temp.class.spec]) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements.

但是,如果我将定义放在源文件中并将其声明留在 header 中,例如,

// "a.h"
template <typename T>
struct S {};

template <>
struct S<int>; // declaration

// "a.cpp"
#include "a.h"

template <>
struct S<int> {}; // definition

// "main.cpp"
#include "a.h"

int main()
{
    S<int> s;
}

然后发生错误(tested by gcc),因为S<int>是一个不完整的类型。

总而言之,我应该把 class 模板的显式特化定义放在哪里?

这是一个定义(特化将被实例化),而不是一个声明,它可能应该放在一个特定的源文件(*.cpp)中:

template <> struct S<int>;

注意:并不是 "hurt" 在每个翻译单元中都有这个...除了编译器实例化所需的时间,以及目标文件的膨胀(*.o 或*.obj).

这是一个声明(特化不会被实例化),放在头文件(*.h)中是可以的:

extern template <> struct S<int>;

声明需要C++11或更高版本。

我将尝试在这里总结一下我通过 the discussion in my other answer 学到的东西,希望能为这个问题留下一个好的答案,而不是让答案埋在评论中。

标准说

an explicit specialization shall be defined at most once in a program (according to ODR)

ODR 是单一定义规则。您只能在一个程序中定义每个 class 一次,但设计允许 class 定义在每个翻译单元中可用的例外情况:您可以在不同的翻译单元中定义 class 作为只要这些不同的定义是相同的,一个字符一个字符。 OP 的引用是 ODR 描述的一部分,请关注 the OP's link 以查看完整描述。

所以 IMO 标准的上面文本意味着显式专业化只能定义一次,但根据 ODR,因此有相同的例外:您可以在头文件中定义它,以便在多个翻译中可用单位。

请注意,在没有完整定义的情况下无法实例化 class(编译器至少需要知道为其分配多少字节)。对于模板化 class 或此类 class 的特化也是如此。因此,必须可以在每个使用它的翻译单元中出现该定义。