私はコピー+スワップのイディオムを実装して、抽象化のレベルを通じて強力な例外の安全性を実現しようとしています。原則は明らかですが、悪魔は詳細にあることがよくあります。
次のようなクラスがあるとします。
class AConcreteType :
public ISomething,
public ISwappable
{
public:
// From ISwappable
void Swap( ISwappable& );
};
ISomethingのみを処理するメソッド内でこれを実行できるようになりました。
void AClass::DoSomething( ISomething& something )
{
// say there is a function that allows me to clone 'something'
// Probably it ought to go into an auto_ptr, but for clarity:
ISomething& somethingElse( clone( something ) );
// ... so that at the end, after doing stuff with somethingElese I can do
ISwappable& swappable1 = dynamic_cast<ISwappable&>( something );
ISwappable& swappable2 = dynamic_cast<ISwappable&>( somethingElse );
// ... I may want to check that the concrete types behind the interface are
// actually the same too with something like typeid, but I'll leave that out for clarity
swappable1.Swap( swappable2 );
}
どこ
void AConcreteType::Swap( ISwappable& swappable )
{
AConcreteType& somethingConcrete = dynamic_cast<AConcreteType&>(swappable);
std::swap( *this, somethingConcrete );
}
すべてのdynamic_castが参照上にあるため、これはすべて機能します。これは、タイプがサポートされていない場合にスローされる操作です。スワップは最後まで行われないため、これによりオブジェクトは良好な状態になります。しかし、私が気に入らないのは、swappable1.Swap(swappable2)の呼び出しが(同じdynamic_castメカニズムを介して)スローできるという事実です。これは、Swapのユーザーにとって、おそらく何も期待しないため、直感に反します。その時点で投げる。
私が考えた別の方法は、Swapの実装内のdynamic_castを廃止するためにISwappableをテンプレート化することでした。
template< typename T >
class ISwappable
{
public:
virtual void Swap( T& ) = 0;
};
そのため、その実装は単純です
class AConcreteType :
public ISomething,
public ISwappable<AConcreteType>
{
void Swap( AConcreteType& act ) { std::swap( *this, act ); }
};
これにより、Swap呼び出しを非スローにすることができます(また、コンパイル時に2つのオブジェクトが実際にスワップ可能であることを保証できます)が、問題は、DoSomething内で具象型を処理する必要があることですが、その関数内のAConcreteTypeにアクセスできます。
何か案は?