2

ここで提案されているように、コピーアンドスワップイディオムを実装しようとしたテンプレートクラスを使用すると、リンカーエラーが発生します。

コピーアンドスワップのイディオムとは何ですか?

テンプレートクラスを「TemplateClass」と呼びましょう。部分的に次のように定義されています。

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

.hファイルに含まれている別のTemplateClass.cppに実装を配置しました。(編集:すべてが.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、そのメソッドの1つで次のような呼び出しを行う場合

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

だから私の質問は:なぜオペレーターはスワップメソッドを見つけられないのですか?

テンプレート引数用にコンパイルされていないようです。コンパイラにフレンドボイドもコンパイルさせることはどういうわけか可能ですか?

私はおそらくfriend voidアプローチを捨てて、クラス内のスワップメソッドとクラス外のスワップメソッドとstd名前空間の1つを定義することができますが、それがそのように機能するかどうかはわかりません。とにかく可能です。


解決:

これは仕事をしました:

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

<T>オカレンスも削除する必要があることに注意してください。

4

2 に答える 2

3

これは、非メンバー関数をテンプレートと組み合わせる場合の一般的な問題です。friend内部の宣言は、テンプレートとは関係ありTemplateClassませんが、テンプレートがインスタンス化されたものを受け取る、テンプレート化されていない無料の関数です(つまり、特殊化は、テンプレート化されていない無料の関数と関係があります)。swapswapTemplateClass<T>TTemplateClass<int>void swap( TemplateClass<int>&,TemplateClass<int>& );

最善の解決策は、クラステンプレート定義内にインライン化された定義を提供することです。これにより、コンパイラは必要に応じて正確な型swapの非テンプレート関数を生成します。swapもう1つのプラスの副作用として、このswap関数は引数依存のルックアップ中にのみ検出されるため、テンプレートを含まないもののオーバーロード解決には関与しません。

他の選択肢は、テンプレート関数全体を友好するか、テンプレートがインスタンス化されたものに適用されたときに関数swapの特定の特殊化を友好することです。最初のオプションはコードが単純ですが、テンプレートのすべての特殊化へのアクセスを許可するため、悪影響が生じる可能性があります。特定のスペシャライゼーションと提携することでその問題は解決しますが、実装は少し複雑になります(クラステンプレートを前方宣言し、次にテンプレートを宣言し、次にクラステンプレートを定義し、最後にテンプレートを定義する必要があります)。swapTswapswapswapswap

これについては、この他の回答で詳しく説明しています。ここでは、さまざまなオプションと構文がより詳細に説明されています。

の特定のエラーメッセージに関してはunresolved external、それは識別子のルックアップがどのように機能するかによるものです。メンバー関数内で使用した場合swap(*this,other);、ルックアップはクラス内で開始され、適切なを見つけようとしますswap。最初にクラスコンテキストを調べて、friendfree関数の宣言を見つけるので、ルックアップは外向きに進み続けず、その特定のfree関数に依存関係を追加します。依存関係を追加し、リンカが適切なシンボルを見つけるのを待ちます。コンパイラーは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 );
    // ...
};

swap純粋主義者になり、「your」 (swap同じテンプレートパラメータを使用)とのみ友達になりたい場合は、追加の作業が必要です。

于 2011-09-07T09:01:26.677 に答える