Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using non-const reference as const in C++

Tags:

c++

constants

I'm trying to use const keyword correctly in my code. I have a class A that have some other classes B in and std::array as member variable.

I have an accessor to get one of A's B member.

  class B
  {
    public:
      int getMember() const;
  };


  class A
  {
    public:
      B& getB(const size_t idx)
      {
        return m_bCollection[idx];
      }

    private:
      std::array<B, 10> m_bCollection;
  };

Then I added a function to serialize an instance of A for yaml-cpp

  YAML::Emitter& operator<<(YAML::Emitter& out, const A& a)
  {
    // some stuff here, and then:
    out << a.getB(1).getMember();

    return out;
  }

This does not compile since the call to getB violates the const modifier for a argument of my serializing function.

error: passing ‘const A’ as ‘this’ argument discards qualifiers

I could overload my getB method to have a constant version of it, but this does not seems very clean...

  B& A::getB(const size_t idx);
  const B& A::getB(const size_t idx) const;

I have this issue on other similar classes as well and have const-overloaded a lot more method. This modification seems a mess to me. I think I missed a cleaner way to achieve my goal (having a const A& as serializer argument).

I only use const members of the non-const B instance reference returned by the const A instance. What should I refactor here to have clean code that follow c++ good practices ?

like image 810
Nicolas Appriou Avatar asked Apr 02 '26 18:04

Nicolas Appriou


1 Answers

I only use const members of the non-const B instance reference returned by the const A instance. What should I refactor here to have clean code that follow c++ good practices ?

The clean idomatic way is the one that you do not want because you consider it as messy. Your A should look like this

class A
  {
    public:
      B& getB(size_t idx)    
      {
        return m_bCollection[idx];
      }
      const B& getB(size_t idx) const {
        return m_bCollection[idx];
      }   
    private:
      std::array<B, 10> m_bCollection;
  };

To avoid code duplication you can use the solution presented here or other answers of that question.

In general you should make methods that do not modify members const by default. Only if you need them non-const supply a second overload (not the other way around). There will be many more cases where you will hit the same wall with your code. Just as one example, an overload for operator<< should have this signature:

std::ostream& operator<<(std::ostream&, const A&); 
                                       // ^--------  const !!!

Last but not least: I suppose it is due to simplification of your example, but your getA looks like an attempt for encapsulation. In fact it is not. Once you returned a non-const reference to a member you can make the member public as well. The only purpose getB serves is that you can write a.getB(index) instead of a.m_bCollection[index] but it does not achieve encapsulation.

like image 156
463035818_is_not_a_number Avatar answered Apr 04 '26 07:04

463035818_is_not_a_number



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!