我正在尝试实现一个复制+交换习语,以通过一定程度的抽象来实现强异常安全,尽管原理很明确,但通常情况下,魔鬼在细节中。
假设我有一个看起来像这样的类:
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_casts 都在引用上,这是一个在不支持类型时抛出的操作;这使我的对象处于良好状态,因为交换直到最后才会发生。但我不满意的是,调用 swappable1.Swap(swappable2) 仍然可以抛出(通过相同的 dynamic_cast 机制),这对于 Swap 的用户来说是违反直觉的,因为他可能不会期待任何事情在那个时候扔。
我想到的另一种方法是模板化 ISwappable,以便在 Swap 的实现中取消 dynamic_cast:
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 调用不被抛出(并允许我保证这两个对象在编译时实际上是可交换的),但现在的问题是我必须处理 DoSomething 中的具体类型,但我没有t 可以访问该函数内的 AConcreteType。
有任何想法吗?