Frage

Bei Verwendung einer Vorlagenklasse, bei der ich versucht habe, das hier vorgeschlagene Copy-and-Swap-Idiom zu implementieren, wird ein Linkerfehler angezeigt:

Wie lautet die Copy-and-Swap-Sprache?

Die Vorlagenklasse, nennen wir sie "TemplateClass", ist teilweise wie folgt definiert:

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

Ich habe die Implementierungen in eine separate TemplateClass.cpp gestellt, die in der .h-Datei enthalten ist. (Bearbeiten: Ich habe das gleiche Problem, wenn sich alles in der .h-Datei befindet)

Der Zuweisungsoperator ist definiert als:

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

und die Swap-Methode ist definiert als:

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

(Keine Sorge, ich nenne meine Mitglieder nicht wirklich "member1" usw.)

Ich habe eine ähnliche Klasse, die auf die gleiche Weise definiert ist, aber keine Vorlagenklasse ist. Dort funktioniert alles gut. Wenn ich jedoch eine Klasse TestClass habe, die einen MitgliedsTemplateClass< HandledClass > member hat, rufe ich eine ihrer Methoden wie auf

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

Ich erhalte einen ungelösten externen Fehler:

LNK2019: Ungelöstes externes Symbol "void __cdecl swap (Klasse TemplateClass &, Klasse TemplateClass &)" (...) in Funktion "public: Klasse TemplateClass X & __thiscall TemplateClass X :: operator= (Klasse TemplateClass)" (. ..) in TestClass.obj

Oder mit anderen Worten: Etwas in TestClass ruft TemplateClass<HandledClass>::operator= auf, der keinen void swap( TemplateClass<HandledClass>, TemplateClass<HandledClass> ) findet.

Meine Frage lautet also: Warum findet der Bediener die Swap-Methode nicht?

Es sieht so aus, als ob es nicht für das Vorlagenargument kompiliert wurde. Ist es irgendwie möglich, dass der Compiler auch Freundlücken kompiliert?

Ich könnte wahrscheinlich den friend void-Ansatz fallen lassen und eine In-Class-Swap-Methode plus eine Out-of-Class-Swap-Methode plus eine im Standard-Namespace definieren, aber ich weiß nicht, ob es so funktionieren würde und ich möchte um dies nach Möglichkeit zu vermeiden.


Lösung:

das hat den Job gemacht:

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

Beachten Sie, wie ich auch die Vorkommen entfernen musste.

War es hilfreich?

Lösung

Dies ist ein häufiges Problem, wenn Sie sich mit Funktionen, die keine Mitglieder sind, mit Vorlagen anfreunden. Die friend-Deklaration innerhalb des TemplateClass ist nicht mit Ihrer swap-Vorlage befreundet, sondern mit einem nicht vorlagenfreien swap, der TemplateClass<T> verwendet, für den T verwendet wird, für welchen TemplateClass<int> die Vorlage instanziiert wird (dh der generierte void swap( TemplateClass<int>&,TemplateClass<int>& ); ist ein freier Funktionsgeneracodetetcode).

Die beste Lösung besteht darin, die swap-Definition bereitzustellen, die in der Klassenvorlagendefinition enthalten ist, da der Compiler dann bei Bedarf eine nicht templatierte swap-Funktion für den genauen Typ generiert. Als weiterer positiver Nebeneffekt wird diese swap-Funktion nur während der argumentabhängigen Suche gefunden, sodass sie nicht an der Überlastungsauflösung für etwas teilnimmt, das Ihre Vorlage nicht betrifft.

Andere Alternativen sind die Freundschaft mit der gesamten swap-Vorlagenfunktion oder die Freundschaft mit der speziellen Spezialisierung der swap-Funktion, wenn sie auf denselben T angewendet wird, mit dem die Vorlage instanziiert wurde. Die erste der Optionen ist einfach im Code, gewährt jedoch Zugriff auf alle Spezialisierungen der swap-Vorlage, was möglicherweise schlimme Nebenwirkungen hat. Wenn Sie sich mit der speziellen swap-Spezialisierung anfreunden, wird dieses Problem gelöst, die Implementierung ist jedoch etwas komplexer (Sie müssen die Klassenvorlage, dann die swap-Vorlage weiterleiten, dann die Klassenvorlage definieren und schließlich die swap-Vorlage definieren).

Mehr dazu in dieser anderen Antwort , wo Die verschiedenen Optionen und Syntaxen werden ausführlicher erläutert.

Die spezielle Fehlermeldung von unresolved external ist darauf zurückzuführen, wie die Suche nach Bezeichnern funktioniert. Wenn Sie swap(*this,other); in einer Mitgliedsfunktion verwendet haben, beginnt die Suche in der Klasse und versucht, einen geeigneten swap zu finden. Es sucht zuerst im Klassenkontext und findet die Deklaration der freien Funktion friend, sodass die Suche nicht weiter nach außen geht und dieser bestimmten freien Funktion eine Abhängigkeit hinzufügt. Es fügt die Abhängigkeit hinzu und wartet, bis der Linker das entsprechende Symbol gefunden hat. Da der Compiler den swap mit Vorlagen nie auf Namespace-Ebene berücksichtigt hat, hat er ihn nie instanziiert, aber selbst wenn er diese Vorlage instanziiert hat, ist die Abhängigkeit innerhalb der Mitgliedsfunktion operator= von einer freien Funktion und nicht von dieser Spezialisierung.

Andere Tipps

Sie sollten entweder die Deklaration der Klassenvorlage in die Header-Datei einfügen oder, wenn Sie im Voraus alle Typen kennen, mit denen diese Klassenvorlage instanziiert wird, eine explizite Instanziierung in der Header-Datei angeben:

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

Es ist langweilig, aber manchmal ist es die beste Option.

In Bezug darauf, warum Ihr Swap nicht verknüpft ist: Sie deklarieren Freundschaften mit einem Nicht-Template-Funktions-Swap, der nicht vorhanden ist. Versuchen Sie Folgendes:

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

Zusätzliche Anstrengungen erforderlich, wenn Sie Purist sein und nur mit 'Ihrem' swap (swap mit denselben Vorlagenparametern) befreundet sein möchten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top