C ++-在模板类之外但在标头中定义成员函数 [英] C++ - Define member function outside template-class but in header

查看:151
本文介绍了C ++-在模板类之外但在标头中定义成员函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我用一个成员函数定义了一个简单的类模板.它是在类外部定义的,带有附加的(显式)专业化名称,该类也在类外部定义.全部集中在一个头文件中.如果在多个翻译单元中包含此标头,则由于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;
};

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

解决方案

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语言规范中,

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

I have defined a simple class-template with one member function. It is defined outside the class with an additional (explicit) specialization, also defined outside the class. All in one headerfile. If you include this header in multiple translation units you get a linker error due to 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;
};

Everything fine so far. But If I put the definition of the member function inside the class body, the linker error disappears and the functions can be used throughout different translation units.

// Header with a template

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

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

My question is why does it work that way? I use MSVC 2012. ODR has some exceptions on templates what I first thought to be the reason. But the definition of the "Base" function inside/outside the class makes the difference here.

解决方案

14.7/5 says

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

  • an explicit instantiation definition shall appear at most once in a program,
  • an explicit specialization shall be defined at most once in a program (according to 3.2), and
  • both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit instantiation follows a declaration of the explicit specialization.

An implementation is not required to diagnose a violation of this rule.

The second bullet applies to your case. The ODR defined in 3.2 says the same thing, albeit in a less distilled form.

Regardless of where and how the non-specialized version of member function is defined, the specialized version definition

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

has to go into a .cpp file. If kept in the header file, it will produce an ODR violation once the header gets included into more than one translation unit. GCC reliably detect this violation. MSVC seems to be less reliable in that regard. But, as the quote above states, an implementation is not required to diagnose a violation of this rule.

The header file should only contain a non-defining declaration of that specialization

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

The fact that in MSVC the error appears or disappears depending on such seemingly unrelated factor as how the non-specialized version of the function is defined must be a quirk of MSVC compiler.


After further research, it appears that MSVC implementation is actually broken: its behavior goes beyond what's allowed by the "no diagnostic is required" permission given by the language specification.

The behavior you observed in your experiments in consistent with the following: declaring the primary function template as inline automatically makes the explicit specialization of that template inline as well. This is not supposed to be that way. In 14.7.3/14 the language specification says

An explicit specialization of a function template is inline only if it is declared with the inline specifier or defined as deleted, and independently of whether its function template is inline.

这篇关于C ++-在模板类之外但在标头中定义成员函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆