未解决的外部使用带有复制和交换的模板类

发布于 2024-12-03 17:08:55 字数 2366 浏览 1 评论 0原文

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

什么是复制和交换习惯用法?

模板类,我们称之为“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 ,它有一个成员 TemplateClassTemplateClassTestClass 。处理类>成员,我在其方法之一中进行调用,就像

void TestClass::setMember( TemplateClass< HandledClass > newObject )
{
    member = newObject;
}

我收到未解决的外部错误一样:

LNK2019:无法解析的外部符号“void __cdecl swap(class TemplateClass &, class TemplateClass &)”(...) in TestClass.obj 中的函数“public: class TemplateClass X & __thiscall TemplateClass X::operator=(class TemplateClass)” (...)

或者换句话说: TestClass 中的某些内容调用 TemplateClass::operator= ,但找不到 void swap( TemplateClass, TemplateClass)

所以我的问题是: 为什么操作员找不到交换方法?

看起来它没有针对模板参数进行编译。是否有可能让编译器也编译朋友 void?

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


解决方案:

这完成了工作:

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

注意我如何删除 < T>的现象也是如此。

I'm getting a linker error when using a template class where I tried implementing the copy-and-swap idiom as suggested here:

What is the copy-and-swap idiom?

The template class, let's call it "TemplateClass" is partially defined like this:

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

I've put the implementations in a separate TemplateClass.cpp which is included in the .h file.
(Edit: I have the same issue if everything is in the .h file)

The assignment operator is defined as:

template< class T >
TemplateClass< T >& TemplateClass< T >::operator= ( TemplateClass< T > other )
{
    // copy-and-swap idiom
    swap( *this, other );
    return *this;
}

and the swap method is defined as:

template< class T >
void swap( TemplateClass< T >& first, TemplateClass< T >& second )
{
    using namespace std;
    swap( first.member1, second.member1 );
    swap( first.member2, second.member2 );
    // ...
}

(Don't worry, I do not really name my members "member1" etc)

I have a similar class which is defined in the same way, but is not a template class. Everything works fine there.
However, if I have a class TestClass which has a member TemplateClass< HandledClass > member and I make a call in one of its methods like

void TestClass::setMember( TemplateClass< HandledClass > newObject )
{
    member = newObject;
}

I get an unresolved external error:

LNK2019: Unresolved external symbol "void __cdecl swap( class TemplateClass &, class TemplateClass &)" (...) in function "public: class TemplateClass X & __thiscall TemplateClass X::operator=(class TemplateClass)" (...) in TestClass.obj

Or in other words:
Something in TestClass calls TemplateClass<HandledClass>::operator= which does not find void swap( TemplateClass<HandledClass>, TemplateClass<HandledClass> ).

So my question is:
Why does the operator not find the swap method?

It looks like it was not compiled for the template argument . Is it somehow possible to have the compiler compile friend voids as well?

I could probably ditch the friend void approach and define an in-class swap method plus an out-of-class swap method plus one in std namespace, but I don't know if it would work that way and I'd like to avoid that if possible anyway.


Solution:

this did the job:

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

Note how i had to remove the < T > occurrences as well.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

怀里藏娇 2024-12-10 17:08:55

当非成员函数与模板友好时,这是一个常见问题。 TemplateClass 内的 friend 声明并不适合您的 swap 模板,而是一个非模板化的自由函数 swap需要 TemplateClass 实例化模板的 T (即,专门化 TemplateClass 将与自由函数 无效swap( TemplateClass&,TemplateClass& ); 未模板化)。

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

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

有关此问题的更多信息,请参阅另一个答案,其中不同的选项并对语法进行了更详细的解释。

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

This is a common problem when befriending non-member functions with templates. The friend declaration inside the TemplateClass does not befriend your swap template, but rather a non-templated free function swap that takes TemplateClass<T> for which ever T the template is instantiated (i.e. the specialization TemplateClass<int> will befriend a free function void swap( TemplateClass<int>&,TemplateClass<int>& ); that is not templated).

The best solution is to provide the swap definition inlined inside the class template definition, as that will make the compiler generate a non-templated swap function for the exact type whenever needed. As another positive side effect, that swap function will only be found during Argument Dependent Lookup, so it will not take part of overload resolution for anything that does not involve your template.

Other alternatives are befriending the whole swap template function, or befriending the particular specialization of the swap function when applied to the same T that the template has been instantiated with. The first of the options is simple in code, but it grants access to all of the specializations of the swap template, and that might have bad side effects. Befriending the particular swap specialization solves that issue, but is a bit more complex to implement (you need to forward declare the class template, then the swap template, then define the class template, and finally define the swap template).

More on this in this other answer, where the different options and syntaxes are explained with more detail.

As to the particular error message of unresolved external, that is due to how identifier lookup works. When you used swap(*this,other); inside a member function, lookup starts inside the class, and tries to find an appropriate swap. It first looks in the class context and finds the declaration of the friend free function, so lookup does not continue going outwards and adds a dependency to that particular free function. It adds the dependency and waits for the linker to locate the appropriate symbol. Because the compiler never considered the templated swap at namespace level, it never actually instantiated it, but even if it had instantiated that template, the dependency inside the operator= member function is on a free function, not that specialization.

一向肩并 2024-12-10 17:08:55

您应该将类​​模板的声明放入头文件中,或者如果您事先知道该类模板将实例化的所有类型,则在头文件中提供显式实例化:

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 );
    // ...
};

如果你想成为一个纯粹主义者并且只与“你的”交换交换<,则需要额外的努力/code> (使用相同的模板参数交换)。

You should either put the declaration of class template into header file, or if you know in advance all types that this class template will be instantiated with, provide explicit instantiation at header file:

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 );

It's tedious but sometimes it's the best option.

Regarding why your swap doesn't link: you declare friendnes with non-template function swap, which doesn't exist.Try this:

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

Additional efforts required if you wanna be a purist and only be friends with 'your' swap (swap with the same template parameters).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文