Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should a member function have a const qualifier and when shouldn't it?

About six years ago, a software engineer named Harri Porten wrote this article, asking the question, "When should a member function have a const qualifier and when shouldn't it?" I found it to be the best write-up I could find of the issue, which I've been wrestling with more recently and which I think is not well covered in most discussions I've found on const correctness. Since a software information-sharing site as powerful as SO didn't exist back then, I'd like to resurrect the question here.

like image 787
SCFrench Avatar asked Mar 17 '10 14:03

SCFrench


People also ask

Why do we require const qualifier?

We use the const qualifier to declare a variable as constant. That means that we cannot change the value once the variable has been initialized. Using const has a very big benefit. For example, if you have a constant value of the value of PI, you wouldn't like any part of the program to modify that value.

When should a function be const?

A function becomes const when the const keyword is used in the function's declaration. The idea of const functions is not to allow them to modify the object on which they are called. It is recommended the practice to make as many functions const as possible so that accidental changes to objects are avoided.

What does const member function qualifier do?

The const qualifier at the end of a member function declaration indicates that the function can be called on objects which are themselves const. const member functions promise not to change the state of any non-mutable data members.

What does it mean to create a const class member function and why would you want to do that?

A const member function is a member function that guarantees it will not modify the object or call any non-const member functions (as they may modify the object). Now getValue() has been made a const member function, which means we can call it on any const objects.


1 Answers

The article seems to cover a lot of basic ground, but the author still has a question about const and non-const overloads of functions returning pointers. Last line of the article is:

Many will probably answer "It depends." but I'd like to ask "It depends on what?"

To be absolutely precise, it depends whether the state of the A object pointee is logically part of the state of this object.

For an example where it is, vector<int>::operator[] returns a reference to an int. The int referand is "part of" the vector, although it isn't actually a data member. So the const-overload idiom applies: change an element and you've changed the vector.

For an example where it isn't, consider shared_ptr. This has the member function T * operator->() const;, because it makes logical sense to have a const smart pointer to a non-const object. The referand is not part of the smart pointer: modifying it does not change the smart pointer. So the question of whether you can "reseat" a smart pointer to refer to a different object is independent of whether or not the referand is const.

I don't think I can provide any complete guidelines to let you decide whether the pointee is logically part of the object or not. However, if modifying the pointee changes the return values or other behaviour of any member functions of this, and especially if the pointee participates in operator==, then chances are it is logically part of this object.

I would err on the side of assuming it is part (and provide overloads). Then if a situation arose where the compiler complains that I'm trying to modify the A object returned from a const object, I'd consider whether I really should be doing that or not, and if so change the design so that only the pointer-to-A is conceptually part of the object's state, not the A itself. This of course requires ensuring that modifying the A doesn't do anything that breaks the expected behaviour of this const object.

If you're publishing the interface you may have to figure this out in advance, but in practice going back from the const overloads to the const-function-returning-non-const-pointer is unlikely to break client code. Anyway, by the time you publish an interface you hopefully have used it a bit, and probably got a feel for what the state of your object really includes.

Btw, I also try to err on the side of not providing pointer/reference accessors, especially modifiable ones. That's really a separate issue (Law of Demeter and all that), but the more times you can replace:

A *getA();
const A *getA() const;

with:

A getA() const; // or const A &getA() const; to avoid a copy
void setA(const A &a);

The less times you have to worry about the issue. Of course the latter has its own limitations.

like image 147
Steve Jessop Avatar answered Sep 21 '22 03:09

Steve Jessop