C++ - 在模板类之外但在头文件中定义成员函数

Mic*_*ble 4 c++ templates one-definition-rule

我用一个成员函数定义了一个简单的类模板。它是在类外定义的,具有额外的(显式)特化,也在类外定义。全部在一个头文件中。如果您在多个翻译单元中包含此标头,则会由于 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;
};
Run Code Online (Sandbox Code Playgroud)

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

// Header with a template

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

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};
Run Code Online (Sandbox Code Playgroud)

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

AnT*_*AnT 5

14.7/5 说

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

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

不需要实现来诊断违反此规则的行为。

第二个要点适用于您的情况。3.2 中定义的 ODR 说明了同样的事情,尽管形式较少。

无论在何处以及如何成员函数的非专业版的定义,在的专门版本定义

template <> bool TestClass<double>::MemberFunction()
{
    return true;
};
Run Code Online (Sandbox Code Playgroud)

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

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

template <> bool TestClass<double>::MemberFunction();
Run Code Online (Sandbox Code Playgroud)

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


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

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

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