2

使用模板类时出现链接器错误,我尝试按照此处的建议实现复制和交换习惯用法:

什么是复制和交换成语?

模板类,我们称之为“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 & __thiscall TemplateClass X::operator=(class TemplateClass)”(...) 中未解析的外部符号“void __cdecl swap(class TemplateClass &, class TemplateClass &)”(...)在 TestClass.obj 中

或者换句话说:TestClass调用TemplateClass<HandledClass>::operator=中找不到的东西void swap( TemplateClass<HandledClass>, TemplateClass<HandledClass> )

所以我的问题是:为什么运营商找不到swap方法?

看起来它不是为模板参数编译的。是否有可能让编译器也编译友元 voids?

我可能会放弃这种friend void方法并定义一个类内交换方法加上一个类外交换方法加上一个 std 命名空间中的一个,但我不知道它是否会这样工作,如果我想避免这种情况反正可能。


解决方案:

这完成了工作:

template< class t >
class TemplateClass
{
    friend void swap( TemplateClass& first, TemplateClass& second )
    {
        // ...
    }
};

请注意我也必须删除 < T > 事件。

4

2 回答 2

3

当使用模板与非成员函数成为朋友时,这是一个常见问题。内部的friend声明TemplateClass不会与您的swap模板成为朋友,而是一个非模板化的自由函数swap,它接受模板被实例化TemplateClass<T>的任何T位置(即,特化TemplateClass<int>将与一个void swap( TemplateClass<int>&,TemplateClass<int>& );未模板化的自由函数成为朋友)。

最好的解决方案是swap在类模板定义中提供内联的定义,因为这将使编译器swap在需要时为确切的类型生成一个非模板函数。作为另一个积极的副作用,该swap函数只会在 Argument Dependent Lookup 期间找到,因此它不会参与任何不涉及模板的重载解决方案。

其他替代方案是与整个模板函数交友,或者在应用于与模板已实例化的相同功能时与函数swap的特定专业化交友。第一个选项的代码很简单,但它允许访问模板的所有特化,这可能会产生不好的副作用。结交特定的专业化解决了这个问题,但实现起来有点复杂(您需要前向声明类模板,然后是模板,然后定义类模板,最后定义模板)。swapTswapswapswapswap

在另一个答案中对此有更多详细信息,其中更详细地解释了不同的选项和语法。

至于 的特定错误消息unresolved external,这是由于标识符查找的工作方式。当您swap(*this,other);在成员函数内部使用时,查找会在类内部开始,并尝试找到合适的swap. 它首先在类上下文中查找friend自由函数的声明,因此查找不会继续向外进行,而是向该特定自由函数添加依赖关系。它添加依赖项并等待链接器找到适当的符号。因为编译器从未考虑过swap命名空间级别的模板化,所以它从未实际实例化过它,但即使它已经实例化了该模板,operator=成员函数内部的依赖关系也在一个自由函数上,而不是那个特化。

于 2011-09-07T09:11:32.623 回答
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 );

这很乏味,但有时它是最好的选择。

关于为什么你的交换不链接:你用不存在的非模板函数交换声明朋友。试试这个:

template< class T >
class TemplateClass
{
    // ...
    TemplateClass< T >& operator= ( TemplateClass< T > other );
    template < class U > friend void swap( TemplateClass< U >& first, TemplateClass< U >& second );
    // ...
};

如果您想成为一个纯粹主义者并且只与“您的” swapswap具有相同的模板参数)成为朋友,则需要额外的努力。

于 2011-09-07T09:01:26.677 回答