This is a continuation of An Alternative Vector, taking a closer look at the copy assignment operator.
In the following code, I am only posting the new version of the copy assignment operator; all the rest of the code is unchanged.
namespace ThorsAnvil
{
// Traits classes
// SimpleCopyableAssignableTraitNoThrow:
// SimpleDestructableTrivialy
// Helper classes
// SimpleDestructableTrivialy
// SimpleCopy
// Forward declare the vector
template<typename T>
class Vector;
Declarations:
template<typename T>
struct SimpleCopyableAssignableTraitNoThrow
{
static constexpr bool value = std::is_nothrow_destructible<T>::value
&& std::is_nothrow_copy_assignable<T>::value;
};
template<typename T>
struct SimpleDestructableTrivialy
{
static constexpr bool value = std::is_trivially_destructible<T>::value;
};
template<typename T, bool = SimpleCopyableAssignableTraitNoThrow<T>::value>
class SimpleCopy
{
public:
void simpleCopy(Vector<T>& dst, Vector<T> const& src) const;
};
template<typename T, bool = SimpleDestructableTrivialy<T>::value>
class SimpleDestroy
{
public:
void destroyElements(Vector<T>& obj);
};
Updated Vector
template<typename T>
class Vector
{
// The rest (see original question for details).
friend class SimpleCopy<T>;
friend class SimpleDestroy<T>;
public:
Vector& operator=(Vector const& value) noexcept(SimpleCopyableAssignableTraitNoThrow<T>::value)
{
SimpleCopy<T> copier;
copier.simpleCopy(*this, value);
return *this;
}
};
SimpleDestroy
template<typename T>
class SimpleDestroy<T, true>
{
public:
// The elements in object are trivially destructible.
// This means nothing is done. As an optimization we
// don't need to do anything to the elements, just
// reset the size
void destroyElements(Vector<T>& obj)
{
obj.length = 0;
}
};
template<typename T>
class SimpleDestroy<T, false>
{
public:
// destructor is non trivial but does not throw
void destroyElements(Vector<T>& obj)
{
for(int loop = 0; loop < obj.length; ++loop)
{
obj.buffer[obj.length - 1 - loop].~T();
}
obj.length = 0;
}
};
SimpleCopy
template<typename T>
class SimpleCopy<T, true>
{
public:
// Neither constructor or destructor throw.
// So we can optimize the copy.
void simpleCopy(Vector<T>& dst, Vector<T> const& src) const
{
if (&dst == &src)
{
return;
}
// Destroy the members of the current object
SimpleDestroy<T> destroyer;
destroyer.destroyElements(dst);
// Copy from the source object to the detonation
// thus reusing the memory underneath. It only tries
// to reallocate if the current object needs to be
// re-sized.
for(auto const& value: src)
{
dst.push(value);
}
}
};
template<typename T>
class SimpleCopy<T, false>
{
public:
// Constructor or Destructor may fail.
// So use Copy & Swap idiom to handle resource
// allocation correctly.
void simpleCopy(Vector<T>& dst, Vector<T> const& src) const
{
Vector<T> tmp(src); // Copy
tmp.swap(dst); // Swap
}
};
}