我在使用模板类时遇到链接器错误,我尝试按照此处的建议实现复制和交换习惯用法:
What is the copy-and-swap idiom?
模板类,我们称之为“TemplateClass”,其部分定义如下:
template< class T >
class TemplateClass
{
// ...
TemplateClass< T >& operator= ( TemplateClass< T > other );
friend void swap( TemplateClass< T >& first, TemplateClass< T >& second );
// ...
};
我已将实现放在单独的TemplateClass.cpp中,该文件包含在.h文件中。 (编辑:如果所有内容都在.h文件中,我会遇到同样的问题)
赋值运算符定义为:
template< class T >
TemplateClass< T >& TemplateClass< T >::operator= ( TemplateClass< T > other )
{
// copy-and-swap idiom
swap( *this, other );
return *this;
}
并且交换方法定义为:
template< class T >
void swap( TemplateClass< T >& first, TemplateClass< T >& second )
{
using namespace std;
swap( first.member1, second.member1 );
swap( first.member2, second.member2 );
// ...
}
(别担心,我并没有真正将我的成员命名为“member1”等)
我有一个类似的类,它以相同的方式定义,但不是模板类。一切都很好。
但是,如果我有一个成员TestClass
的班级TemplateClass< HandledClass > member
,我会使用其中一个方法拨打电话,例如
void TestClass::setMember( TemplateClass< HandledClass > newObject )
{
member = newObject;
}
我收到一个未解决的外部错误:
LNK2019:函数“public:class TemplateClass X&amp; __thiscall TemplateClass X :: operator =(class TemplateClass)”中未解析的外部符号“void __cdecl swap(类TemplateClass&amp;,类TemplateClass&amp;)”(...) “(...)在TestClass.obj中
或换句话说:
TestClass
调用TemplateClass<HandledClass>::operator=
的内容未找到void swap( TemplateClass<HandledClass>, TemplateClass<HandledClass> )
。
所以我的问题是: 为什么操作员找不到交换方法?
看起来它没有为模板参数编译。是否有可能让编译器编译朋友空洞?
我可能放弃friend void
方法并定义一个类内交换方法加上一个类外交换方法加一个std命名空间中的一个,但我不知道它是否会以这种方式工作无论如何,我想尽可能避免这种情况。
解决方案:
这完成了这项工作:
template< class t >
class TemplateClass
{
friend void swap( TemplateClass& first, TemplateClass& second )
{
// ...
}
};
请注意我必须删除&lt; T>事件也是如此。
答案 0 :(得分:3)
使用模板与非成员函数建立联系时,这是一个常见问题。 friend
内的TemplateClass
声明与您的swap
模板不相关,而是一个非模板化的免费函数swap
,其TemplateClass<T>
为T
TemplateClass<int>
void swap( TemplateClass<int>&,TemplateClass<int>& );
1}}模板被实例化(即,特化swap
将与未模板化的自由函数swap
成为朋友。
最佳解决方案是在类模板定义中提供内联的swap
定义,因为这将使编译器在需要时为精确类型生成非模板化swap
函数。另一个积极的副作用是swap
函数只能在Argument Dependent Lookup中找到,因此对于任何不涉及模板的内容,它都不会参与重载解析。
其他替代方案是与整个T
模板函数建立联系,或者在应用于已经实例化模板的同一swap
时与swap
函数的特定特化结合。第一个选项在代码中很简单,但它允许访问{em>所有的swap
模板的特化,这可能会产生不良副作用。结合特定的swap
专业化解决了这个问题,但实现起来有点复杂(你需要转发声明类模板,然后是unresolved external
模板,然后定义类模板,最后定义swap(*this,other);
模板)。
在其他answer中有更多内容,其中详细解释了不同的选项和语法。
关于swap
的特定错误消息,这是由于标识符查找的工作原理。在成员函数中使用friend
时,查找在类中开始,并尝试查找适当的swap
。它首先在类上下文中查找并查找operator=
自由函数的声明,因此查找不会继续向外并向该特定自由函数添加依赖项。它添加依赖关系并等待链接器找到适当的符号。因为编译器从未在命名空间级别考虑模板{{1}},所以它实际上从未实例化它,但即使它已实例化该模板,{{1}}成员函数内的依赖关系也是自由函数,而不是专业化。
答案 1 :(得分:0)
您应该将类模板的声明放入头文件中,或者如果您事先知道将要实例化此类模板的所有类型,请在头文件中提供显式实例化:
template< class T >
class TemplateClass
{
// ...
TemplateClass< T >& operator= ( TemplateClass< T > other );
friend void swap( TemplateClass< T >& first, TemplateClass< T >& second );
// ...
};
template class TemplateClass<FirstType>;
template class TemplateClass<SecondType>;
// ...
// and the same for swap function
template void swap<FirstType>( TemplateClass<FirstType>& first, TemplateClass<FirstType>& second );
template void swap<SecondType>( TemplateClass<SecondType>& first, TemplateClass<SecondType>& second );
这很乏味但有时它是最好的选择。
关于为什么你的交换没有链接:你用非模板函数swap声明了友元,这不存在。试试这个:
template< class T >
class TemplateClass
{
// ...
TemplateClass< T >& operator= ( TemplateClass< T > other );
template < class U > friend void swap( TemplateClass< U >& first, TemplateClass< U >& second );
// ...
};
如果您想成为一名纯粹主义者并且仅与“您的”swap
(swap
具有相同模板参数的朋友)成为朋友,则需要额外的努力。