考虑以下标头和源文件:
// 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
文件中定义专业化并正确链接? 答案 0 :(得分:15)
虽然WhozCraig的答案(现已删除)提供了正确的代码来解决您的问题,但以下是您的问题的一些直接答案,包括对您的代码的评论:
foo<int>()
和foo<float>()
是成员模板的显式特化。这些不得出现在它们所属类的定义中。标准说:
(§14.7.3/ 3)[...]类或类模板的定义应在声明类或类模板的成员模板的显式特化之前。 [...]
所以你必须把它们放在课程定义之后。
对于foo<int>
的情况,它在头文件中完全定义,这意味着您必须在定义之前放置单词inline
;否则如果头文件包含在多个翻译单元中,您将遇到链接器的问题。
foo<float>()
的特化在一个单独的文件中定义,该文件稍后链接到main.cpp
。这是可能的,但它要求在头文件中给出声明(你已经这样做了,但你必须在类定义之外做):
template <>
void MyClass::foo<float>();
这是必需的,因为标准中的另一个陈述:
(§14.7.3/ 6)如果一个模板,一个成员模板或一个类模板的成员被明确地专门化,那么该特化应该在第一次使用该特化之前声明,这将导致隐式实例化发生,在每个发生这种用途的翻译单位;无需诊断。 [...]
由于所有特化都是显式(即完全特化,使用你的词),因此不需要显式实例化,但它们是可能的(§14.7.2)。您可以将它们放在.cpp文件的末尾,语法为:
template void MyClass::foo<float>();
template void MyClass::foo<int>();
同样,这仅对不具有自己的显式特化的类型非常有用。
标题和实现文件的正确代码如下所示:
.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>();