Home > front end >  Implementing the assignment operator in an abstract base class using the curiously recurring templat
Implementing the assignment operator in an abstract base class using the curiously recurring templat

Time:10-13

I am writing an abstract CRTP-based abstract base class for static/dynamic arrays. I intend to put as many methods as possible in the base class so that there is no code duplication in the derived classes. I have got the indexing operator working but I'm struggling with the assignment (=) operator.

/** Array base class. */
template <class t_derived_class, class data_type>
class ArrayBase
{
  private:
  t_derived_class& Derived;

  public:
  /** Default constructor. */
  ArrayBase() : Derived(static_cast<t_derived_class&>(*this)) {}

  /** Pure virtual desctructor. */
  virtual ~ArrayBase() {};

  /** Index operator overloads. */
  data_type& operator[](const std::size_t _index)
  {
    return *(Derived.begin()   _index);
  }

  const data_type& operator[](const std::size_t _index) const
  {
    return *(Derived.begin()   _index);
  }

  /** Assignment operator overloads. */
  t_derived_class& operator=(const t_derived_class& _other_derived)
  {
    for(std::size_t i = 0; i < Derived.size();   i) Derived[i] = _other_derived[i];
    return Derived;
  }
};
/** Static array class. */
template <class data_type, int array_size>
class StaticArray : public std::array<data_type, array_size>, public ArrayBase<StaticArray<data_type, array_size>, data_type>
{
  using Base = ArrayBase<StaticArray<data_type, array_size>, data_type>;
  friend Base;

  public:
  /** Default constructor. */
  StaticArray() : std::array<data_type, array_size>() {}

  /** Default destructor. */
  ~StaticArray() = default;

  using Base::operator[];
  using Base::operator=;
};

/** Dynamic array class. */
template <class data_type>
class DynamicArray : public std::vector<data_type>, public ArrayBase<DynamicArray<data_type>, data_type>
{
  // ...
};
int main()
{
  StaticArray<double, 3> array1;
  array1[0] = 1.0;
  array1[1] = 2.0;
  array1[2] = 3.0;

  // This code compiles
  StaticArray<double, 3> array2 = array1;

  // This code does not compile
  array2 = array1; 

  return 0;
}

My IDE (CLion) gives me the following error when I use the assignment operator as above:

Object of type 'StaticArray<Apeiron::Float, 3>' (aka 'StaticArray<double, 3>') cannot be assigned because its copy assignment operator is implicitly deleted

The compiler errors are:

error: ‘Apeiron::StaticArray<double, 3>& Apeiron::StaticArray<double, 3>::operator=(const Apeiron::StaticArray<double, 3>&)’ cannot be overloaded with ‘t_derived_class& Apeiron::ArrayBase<t_derived_class, data_type>::operator=(const t_derived_class&) [with t_derived_class = Apeiron::StaticArray<double, 3>; data_type = double]’
   90 | class StaticArray : public std::array<data_type, array_size>, public ArrayBase<StaticArray<data_type, array_size>, data_type>
      |       ^~~~~~~~~~~
Array.h:62:20: note: previous declaration ‘t_derived_class& Apeiron::ArrayBase<t_derived_class, data_type>::operator=(const t_derived_class&) [with t_derived_class = Apeiron::StaticArray<double, 3>; data_type = double]’
   62 |   t_derived_class& operator=(const t_derived_class& _other_derived)

Can anyone advise me on how I can get this to work?

CodePudding user response:

Since the member variables of your ArrayBase are reference, the implicitly-declared ArrayBase::operator= will be automatically deleted.

The alternative is to remove the member variables and directly use the help function to get the reference of the derived class:

template <class t_derived_class, class data_type>
class ArrayBase
{
  private:
  t_derived_class& Derived() noexcept {
    return static_cast<t_derived_class&>(*this);
  };

  const t_derived_class& Derived() const noexcept {
    return static_cast<const t_derived_class&>(*this);
  };

  public:
  /** Pure virtual desctructor. */
  virtual ~ArrayBase() {};

  /** Index operator overloads. */
  data_type& operator[](const std::size_t _index)
  {
    return *(Derived().begin()   _index);
  }

  // ...
};
  • Related