"Destructor already defined" 有专门的析构函数

"Destructor already defined" with a specialized destructor

我正在试验专门的析构函数。这段代码完全有效并且编译得很好:

#include <iostream>

using namespace std;

template <typename T>
class Cat
{
public:
    ~Cat();
};

template <typename T>
Cat<T>::~Cat()
{
    std::cout << "T" << std::endl;
}

template <>
Cat<int>::~Cat()
{
    std::cout << "int" << std::endl;
}


int main()
{
    Cat<int> c1;
    Cat<float> c2;
    return 0;
}

但是,如果我将 class 和析构函数放在单独的文件 "Cat.h" 中,并在 Main.cpp 中执行 #include "Cat.h",我会收到链接器错误:LNK2005 "public: __thiscall Cat<int>::~Cat<int>(void)" (??1?$Cat@H@@QAE@XZ) already defined in Cat.obj。为什么?

很可能您的 Cat.cppmain.cpp 翻译单元都包含相同的 Cat.h 头文件。将整个模板 class 放在头文件中,删除 Cat.cpp 翻译单元并在没有它的情况下进行编译。
Live example
此 SO post:
中的更多详细信息 Why can templates only be implemented in the header file?.

听起来你有以下情况:

template <>
Cat<int>::~Cat()
{
    std::cout << "int" << std::endl;
}

在包含在两个翻译单元中的头文件中。函数模板的显式特化是非内联函数 (C++14 [temp.expl.spec]/12);所以这是 ODR 违规。

如果你在同一个地方实现了一个非模板成员函数(或一个自由函数),你会得到同样的错误:两个翻译单元最终都有一个函数的副本,这甚至是不允许的如果两个副本相同。

要解决此问题,请将关键字 inline 放在 template<> 之前。


评论中有一些关于为 class 模板的成员函数提供显式专业化的合法性的讨论。我相信这是正确的,根据 C++14 [temp.expl.spec]/6:

进行限制

If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required.

此外,第 7 节:

The placement of explicit specialization declarations for function templates, class templates, variable templates, member functions of class templates, static data members of class templates, member classes of class templates, member enumerations of class templates, member class templates of class templates, member function templates of class templates, static data member templates of class templates, member functions of member templates of class templates, member functions of member templates of non-template classes, static data member templates of non-template classes, member function templates of member classes of class templates, etc., and the placement of partial specialization declarations of class templates, variable templates, member class templates of non-template classes, static data member templates of non-template classes, member class templates of class templates, etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

在您的程序中,它位于完美的位置:紧接在 class 定义之后。当 class 可能在声明至少遇到专业化之前实例化时,就会发生危险情况。