Неразрешенное внешнее использование шаблонного класса с копированием и заменой - PullRequest
2 голосов
/ 07 сентября 2011

Я получаю ошибку компоновщика при использовании класса шаблона, когда я пытался реализовать идиому копирования и замены, как предложено здесь:

Что такое идиома копирования и обмена?

Класс шаблона, назовем его «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: Неразрешенный внешний символ "void __cdecl swap (class TemplateClass &, class TemplateClass &)" (...) в функции "public: class TemplateClass X & __thiscall TemplateClass X :: operator = (класс TemplateClass)" (. ..) в 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 )
    {
        // ...
    }
};

Обратите внимание, как мне также пришлось удалять вхождения .

Ответы [ 2 ]

3 голосов
/ 07 сентября 2011

Это распространенная проблема при добавлении функций, не являющихся членами, с шаблонами.Объявление friend внутри TemplateClass не поддерживает ваш шаблон swap, а скорее не шаблонную бесплатную функцию swap, для которой требуется TemplateClass<T>, для которой когда-либо создается T шаблон (то есть специализация TemplateClass<int> подружится со свободной функцией void swap( TemplateClass<int>&,TemplateClass<int>& );, которая не является шаблонной.)

Лучшее решение - предоставить определение swap, встроенное в определение шаблона класса, поскольку это заставит компилятор генерировать нешаблонная swap функция для точного типа при необходимости.Другим положительным побочным эффектом является то, что функция swap будет обнаружена только во время зависимого поиска аргумента, поэтому она не будет принимать участие в разрешении перегрузки для всего, что не связано с вашим шаблоном.

Другие альтернативы оказывают поддержку всемуФункция шаблона swap или поддержка конкретной специализации функции swap при применении к тому же T, с которым был создан экземпляр шаблона.Первый из параметров прост в коде, но он предоставляет доступ к всем специализациям шаблона swap, и это может иметь плохие побочные эффекты.Поддержка конкретной специализации swap решает эту проблему, но ее немного сложнее реализовать (вам нужно заранее объявить шаблон класса, затем шаблон swap, затем определить шаблон класса и, наконец, определить шаблон swap).

Подробнее об этом в этом другом ответе , где различные опции и синтаксисы объясняются более подробно.

Что касается конкретного сообщения об ошибке unresolved externalЭто связано с тем, как работает поиск идентификатора.Когда вы используете swap(*this,other); внутри функции-члена, поиск начинается внутри класса и пытается найти соответствующий swap.Сначала он просматривает контекст класса и находит объявление свободной функции friend, поэтому поиск не продолжает расширяться и добавляет зависимость к этой конкретной свободной функции.Он добавляет зависимость и ждет, когда компоновщик найдет соответствующий символ.Поскольку компилятор никогда не рассматривал шаблонное swap на уровне пространства имен, он фактически никогда не создавал его экземпляра, но даже если он создал этот шаблон, зависимость внутри функции-члена operator= является свободной функцией, а не этой специализацией.

0 голосов
/ 07 сентября 2011

Вы должны либо поместить объявление шаблона класса в файл заголовка, либо, если вы заранее знаете все типы, с которыми будет создаваться этот шаблон класса, предоставить явное создание экземпляра в файле заголовка:

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

Требуются дополнительные усилия, если вы хотите быть пуристом и дружить только с «вашим» swap (swap с теми же параметрами шаблона).

...