4

我已经定义了一个带有一个成员函数的简单类模板。它是在类外定义的,具有额外的(显式)特化,也在类外定义。全部在一个头文件中。如果您在多个翻译单元中包含此标头,则会由于 One-Definition-Rule 导致链接器错误。

// Header with a template

template <class T>
class TestClass
{
public:
    TestClass() {};
    ~TestClass() {};
    bool MemberFunction();
};

template <class T>
bool TestClass<T>::MemberFunction()
{
    return true;
}

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};

到目前为止一切都很好。但是如果我将成员函数的定义放在类体内,链接器错误就会消失,并且这些函数可以在不同的翻译单元中使用。

// Header with a template

template <class T>
class TestClass
{
public:
    TestClass() {};
    ~TestClass() {};
    bool MemberFunction()
    {
        return true;
    }
};

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};

我的问题是为什么它会这样工作?我使用 MSVC 2012。ODR 在模板上有一些例外,我最初认为这是原因。但是类内部/外部的“Base”函数的定义在这里有所不同。

4

1 回答 1

5

14.7/5 说

5 对于给定的模板和给定的一组模板参数,

  • 一个显式的实例化定义在程序中最多出现一次,
  • 一个显式的特化应在程序中最多定义一次(根据 3.2),并且
  • 显式实例化和显式特化的声明都不应出现在程序中,除非显式实例化遵循显式特化的声明。

诊断违反此规则的情况不需要实现。

第二个项目符号适用于您的情况。3.2 中定义的 ODR 也说了同样的话,尽管是以一种不那么精炼的形式。

不管成员函数的非特化版本在哪里以及如何定义,特化版本定义

template <> bool TestClass<double>::MemberFunction()
{
    return true;
};

必须进入一个.cpp文件。如果保留在头文件中,一旦头文件包含在多个翻译单元中,就会产生 ODR 违规。GCC 可靠地检测到这种违规行为。MSVC 在这方面似乎不太可靠。但是,正如上面的引文所述,诊断违反此规则的情况不需要实现。

头文件应该只包含该专业化的非定义声明

template <> bool TestClass<double>::MemberFunction();

在 MSVC 中,错误的出现或消失取决于函数的非专业版本的定义方式等看似无关的因素,这一事实一定是 MSVC 编译器的怪癖。


经过进一步的研究,似乎 MSVC 实现实际上被破坏了:它的行为超出了语言规范给出的“不需要诊断”许可所允许的范围。

您在实验中观察到的行为与以下内容一致:将主函数模板声明为inline自动使该模板显式特化inline。这不应该是这样的。在 14.7.3/14 中,语言规范说

仅当使用 inline 说明符声明或定义为已删除时,函数模板的显式特化才是内联的,并且与其函数模板是否内联无关。

于 2014-10-10T18:11:06.413 回答