模板方法专门化链接错误

时间:2012-12-29 03:12:10

标签: c++ templates c++03

考虑以下标头和源文件:

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

int main()
{
  MyClass m;
  m.foo<double>();
  m.foo<float>();
}

// myClass.h
#pragma once

#include <iostream>

using namespace std;

class MyClass
{
public:

  template <typename T>
  void foo()
  {
    cout << "Template function<T> called" << endl;
  }

  template <>
  void foo<int>()
  {
    cout << "Template function<int> called" << endl;
  }

  template <>
  void foo<float>();

};

// myClass.cpp
#include "myClass.h"

template <>
void MyClass::foo<float>()
{
  cout << "Template function<float> called" << endl;
}

我在foo<float>专业化时遇到链接错误。如果我将专门化的定义放在头文件中,那么一切都按预期工作。

我认为原因可能是该方法未被显式实例化(尽管template class的完全特化不需要显式实例化以进行正确的链接)。如果我尝试显式实例化该方法,我会收到此错误:

  

错误C3416:'MyClass :: foo':显式特化可能无法显式实例化

所以问题是:

  • 有没有办法在cpp文件中定义专业化并正确链接?
  • 如果没有,为什么不呢?我可以显式实例化那些模板方法 不专业就好了。为什么不完全专业化?

1 个答案:

答案 0 :(得分:15)

虽然WhozCraig的答案(现已删除)提供了正确的代码来解决您的问题,但以下是您的问题的一些直接答案,包括对您的代码的评论:

  1. foo<int>()foo<float>()是成员模板的显式特化。这些不得出现在它们所属类的定义中。标准说:

      

    (§14.7.3/ 3)[...]类或类模板的定义应在声明类或类模板的成员模板的显式特化之前。 [...]

    所以你必须把它们放在课程定义之后。

  2. 对于foo<int>的情况,它在头文件中完全定义,这意味着您必须在定义之前放置单词inline;否则如果头文件包含在多个翻译单元中,您将遇到链接器的问题。

  3. foo<float>()的特化在一个单独的文件中定义,该文件稍后链接到main.cpp。这是可能的,但它要求在头文件中给出声明(你已经这样做了,但你必须在类定义之外做):

      template <>
      void MyClass::foo<float>();
    

    这是必需的,因为标准中的另一个陈述:

      

    (§14.7.3/ 6)如果一个模板,一个成员模板或一个类模板的成员被明确地专门化,那么该特化应该在第一次使用该特化之前声明,这将导致隐式实例化发生,在每个发生这种用途的翻译单位;无需诊断。 [...]

  4. 由于所有特化都是显式(即完全特化,使用你的词),因此不需要显式实例化,但它们是可能的(§14.7.2)。您可以将它们放在.cpp文件的末尾,语法为:

    template void MyClass::foo<float>();
    template void MyClass::foo<int>();
    

    同样,这仅对具有自己的显式特化的类型非常有用。

  5. 标题和实现文件的正确代码如下所示:

    .h文件:

    class MyClass
    {
    public:
      template <typename T> void foo()
      { cout << "Template function<T> called" << endl; }
    };
    
    template <> inline void MyClass::foo<int>()
    { cout << "Template function<int> called" << endl; }
    
    template <> void MyClass::foo<float>();
    

    的.cpp:

    #include "myClass.h"
    
    template <> void MyClass::foo<float>()
    { cout << "Template function<float> called" << endl; }
    
    /* This is unnecessary for float, but may be useful for
       types that do not have their own explicit specializations: */
    template void MyClass::foo<float>();