Pergunta

Estou recebendo um erro de vinculador ao usar uma classe de modelo em que tentei implementar o idioma copiar e trocar conforme sugerido aqui:

Qual é o idioma de copiar e trocar? < / p>

A classe de modelo, vamos chamá-la de "TemplateClass", é parcialmente definida assim:

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

Coloquei as implementações em um TemplateClass.cpp separado que está incluído no arquivo .h. (Editar: tenho o mesmo problema se tudo estiver no arquivo .h)

O operador de atribuição é definido como:

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

e o método de troca é definido como:

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

(Não se preocupe, eu realmente não chamo meus membros de "membro1" etc)

Eu tenho uma classe semelhante que é definida da mesma maneira, mas não é uma classe de modelo. Tudo funciona bem lá. No entanto, se eu tiver uma classe TestClass que tem um membro TemplateClass< HandledClass > member e faço uma chamada em um de seus métodos, como

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

Recebo um erro externo não resolvido:

LNK2019: Símbolo externo não resolvido "void __cdecl swap (class TemplateClass &, class TemplateClass &)" (...) na função "public: class TemplateClass X & __thiscall TemplateClass X :: operator= (class TemplateClass)" (. ..) em TestClass.obj

Ou em outras palavras: Algo em TestClass chama TemplateClass<HandledClass>::operator=, que não encontra void swap( TemplateClass<HandledClass>, TemplateClass<HandledClass> ).

Então, minha pergunta é: Por que o operador não encontra o método de troca?

Parece que não foi compilado para o argumento do modelo. É de alguma forma possível fazer com que o compilador compile voids de amigo também?

Eu provavelmente poderia abandonar a abordagem friend void e definir um método de troca dentro da classe mais um método de troca fora da classe mais um no namespace std, mas não sei se funcionaria dessa forma e gostaria para evitar isso, se possível, de qualquer maneira.


Solução:

isso fez o trabalho:

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

Observe como eu tive que remover as ocorrências também.

Foi útil?

Solução

Este é um problema comum ao fazer amizade com funções não membros com modelos. A declaração friend dentro do TemplateClass não faz amizade com seu modelo swap, mas em vez disso, um swap de função livre não modelado que leva TemplateClass<T> para qualquer T o modelo é instanciado (ou seja, o TemplateClass<int> de função livre não modelado será amigo de um void swap( TemplateClass<int>&,TemplateClass<int>& ); de função livre).

A melhor solução é fornecer a definição swap embutida na definição do modelo de classe, pois isso fará com que o compilador gere uma função swap não modelada para o tipo exato sempre que necessário. Como outro efeito colateral positivo, a função swap só será encontrada durante a Pesquisa Dependente do Argumento, portanto, não fará parte da resolução de sobrecarga para qualquer coisa que não envolva seu modelo.

Outras alternativas são fazer amizade com toda a função de template swap, ou fazer amizade com a especialização particular da função swap quando aplicada ao mesmo T com o qual o template foi instanciado. A primeira das opções é simples no código, mas concede acesso a todas as especializações do modelo swap, e isso pode ter efeitos colaterais ruins. Fazer amizade com a especialização swap particular resolve esse problema, mas é um pouco mais complexo de implementar (você precisa declarar o modelo de classe, o modelo swap, definir o modelo de classe e, finalmente, definir o modelo swap).

Mais sobre isso nesta outra resposta , onde as diferentes opções e sintaxes são explicadas com mais detalhes.

Quanto à mensagem de erro específica de unresolved external, é devido a como funciona a pesquisa de identificador. Quando você usa swap(*this,other); dentro de uma função de membro, a pesquisa começa dentro da classe e tenta encontrar um swap apropriado. Ele primeiro examina o contexto da classe e encontra a declaração da função free friend, portanto, a pesquisa não continua indo para fora e adiciona uma dependência a essa função livre específica. Ele adiciona a dependência e espera que o vinculador localize o símbolo apropriado. Como o compilador nunca considerou o swap modelado no nível do namespace, ele nunca o instanciou, mas mesmo se tivesse instanciado esse modelo, a dependência dentro da função de membro operator= está em uma função livre, não naquela especialização.

Outras dicas

Você deve colocar a declaração do modelo de classe no arquivo de cabeçalho ou, se souber com antecedência todos os tipos com os quais esse modelo de classe será instanciado, forneça uma instanciação explícita no arquivo de cabeçalho:

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

É tedioso, mas às vezes é a melhor opção.

Sobre o motivo pelo qual sua troca não vincula: você declara amigos com troca de função não-modelo, que não existe.

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

Esforços adicionais são necessários se você quer ser um purista e ser amigo apenas de 'seu' swap (swap com os mesmos parâmetros de modelo).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top