1

Can anyone explain me this error? Here is the code:

class O{
    unsigned x;
    unsigned y;
    public:
        O(unsigned x_ ,unsigned  y_): x(x_), y(y_){
        };
        O& operator+= ( O & object){
            x += object.x;
        }
};

class A {
    O item;
    public:
        A(O i): item(i){;
        }
        void fun() const{
            O j(2,3);
            j += item;
        }
};

int main(){
    return 0;
}

When I try to compile I get this error:

In member function 'void A::fun() const':
[Error] no match for 'operator+=' (operand types are 'O' and 'const O')
[Note] candidate is:
[Note] O& O::operator+=(O&)
[Note] no known conversion for argument 1 from 'const O' to 'O&'

Can anyone explain me this? If I add const qualifier to the argument of the += operator, it compiles. So I think the problem is that I'm passing a reference to item to the += opertor, which is non const, inside the const function fun(). Can anyone explain me why this is illegal, and how I can avoid to do this kind of mistakes, if for instance there is some rule of thumb to follow when using const qualifiers, etc..?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Peanojr
  • 185
  • 1
  • 8
  • 2
    Inside of a const-qualified function (`void fun() const`), every class field is considered to be `const`. Non-const references can't bind to const things. – HolyBlackCat Dec 09 '19 at 19:07
  • Calling `j += item` in a const method makes `item` constant and thus requires an implementation of the operator that takes a `const` reference. – imreal Dec 09 '19 at 19:09

2 Answers2

4

This member function

  void fun() const{
        O j(2,3);
        j += item;
    }

is a constant member function. So members of the object to which the function is called are considered as constant members in particular the data member item in this case is considered as it was declared like

const O item;

In this expression

j += item;

there is used the member function

    O& operator+= ( O & object){
        x += object.x;
    }

that accepts a non-constant reference to an object of the type O. So in fact you are trying to bind a non-constant reference to a constant object. So the compiler issues an error.

The parameter of the above operator should be declared with the qualifier const and has a return statement as for example

    O& operator+= ( const O & object) {
        x += object.x;
        return *this;
    }
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Yes, I forgot the return. Since we are here I would ask another strictly related question. Why I cannot initialize a plain reference with a reference to const? You cannot bind a plain reference to a const objects, since you would allow to modify that, but I don't see how this reasoning extends to two references. – Peanojr Dec 09 '19 at 19:26
  • @Peanojr Using a non-constant reference you can change the object referenced by the const reference. that would invoke undefined behavior. – Vlad from Moscow Dec 09 '19 at 19:28
  • So it's like I'm undirectly allowing to change the object the const reference points to, by assigning the const reference to a non const reference? – Peanojr Dec 09 '19 at 19:35
3

In many places the const keyword is a promise that [some code] wont chjange the state of the thing marked const. By contrast the lack of the keyword had to be interpreted as meaning [some code] might change the state of that thing.

The compiler checks to see if these promises are consitent, and complains if they are not.

In this case you have a declaration

O& O::operator+= (O & object);

where you don't promise to keep the argument object constant. And because this is a reference argument you are not promising to leave the things refered to alone.

But you try to call that operation from the context

void A::fun() const;

which does promise to keep the state of the calling object constant and then uses one of it's members (A::item) as the argument to O::operator+.

The compiler is complaining that you promised in one place to keep A::item constant and in another have not made that guarantee.

becasuse O::operator+= doesn't actually change it's argument, you can fix the issue by changing the signature to

O& O::operator+= (const O & object);

Done and dusted.

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
  • This procedure is meant to avoid to inadvertently modify class members, right? It looks to me the bugs caused by const quaifiers are more than the "unseen mistakes" you would have without it.. – Peanojr Dec 09 '19 at 19:45
  • Preventing mistakes would be a good enough reason for it, but it *also* enables compiler optiminzation of the code. – dmckee --- ex-moderator kitten Dec 09 '19 at 20:01