Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any alternatives to making const version of class?

In C++ I'm often facing a situation when I need to prepare const and non-const version of class in analogy to const_iterator and iterator from standard library.

class const_MyClass
{
  public:
      const_MyClass(const int * arr):
         m_arr(arr)
      {
      }

      int method() const;  //does something with m_arr without modifying it

  private:
      const int * m_arr;
}

class MyClass
{
  public:
      MyClass(int * arr):
         m_arr(arr)
      {
      }

      int method() const;  //does something with m_arr without modifying it

      void modify(int i);  //modify m_arr

  private:
      int * m_arr;
}

The problem with this is that I need to repeat whole code of const_MyClass in MyClass and distribute any changes in API to both classes. Thus sometimes I inherit const_MyClass and do some const_casts, which also isn't perfect and pretty solution. Still when I want to pass const_MyClass instance by reference it looks moronic:

void func(const const_MyClass & param)

Instance param is marked with two "consts", and it has only const methods...

This is where const constructors would be handy, but are there any existing alternatives?


Some use examples to explain problem better:

//ok to modify data
void f(int * data)
{
    MyClass my(data);
    my.modify();
    ...
}

//cant modify data, cant use MyClass
void fc(const int * data)
{
    const_MyClass my(data);
    int i = my.method();
    ...
}
like image 290
mip Avatar asked Dec 10 '25 05:12

mip


2 Answers

You can make a template class to act as a base, like this:

template<typename T>
class basic_MyClass
{
  public:
      basic_MyClass(T * arr) :m_arr(arr) {}    
      int method() const;  //does something with m_arr without modifying it    
  private:
      T * m_arr;
};

Then, for your const version, since it doesn't add anything, you can just use a typedef:

typedef basic_MyClass<const int> const_MyClass;

For your non-const version, you can inherit:

class MyClass : public basic_MyClass<int>
{
  public:
    using basic_MyClass::basic_MyClass; // inherit all the constructors
    void modify(int i);  //modify m_arr
};
like image 166
Benjamin Lindley Avatar answered Dec 13 '25 03:12

Benjamin Lindley


Have you considered simply tracking two pointers and raising exceptions from the mutable operations when no mutable value is available? Maybe an example will help describe what I am thinking of.

class MyClass
{
public:
    MyClass(int *mutable_data):
        m_mutable_view(mutable_data), m_readonly_view(mutable_data) 
    {
    }

    MyClass(const int *immutable_data):
        m_mutable_view(NULL), m_readonly_view(immutable_data) 
    {
    }

    int retrieve_value(int index) {
        return m_readonly_view[index];
    }

    void set_value(int index, int value) {
        require_mutable();
        m_mutable_view[index] = value;
    }

protected:
    void require_mutable() {
        throw std::runtime_error("immutable view not available");
    }

private:
    const int *m_readonly_view;
    int *m_mutable_view;
};

The idea is pretty simple here - use a sentinel value to indicate whether modifications are possible or not instead of depending on the type system to do that for you. Personally, I would think about doing the inheritance based approach that @BenjaminLindley suggested but I wanted to present a slightly different solution that might not have occurred to you.

like image 25
D.Shawley Avatar answered Dec 13 '25 03:12

D.Shawley



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!