Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using or not using const on methods that modify / allow the modification of referenced values without touching the fields themselves

I'm not sure to entirely understand the philosophy behind the const keyword used on class methods.

I've always thought that using the const keyword in the signature of a class method meant that this method would not modify the fields of the object it's called on. However when we use for example a vector, there is an overload for operator [] that is not const :

whateverT & vector<whateverT>::operator [] ( size_t pos )

No matter what you do with the reference you are given, it will perform no modifications on the vector's fields, even if the referenced item is modified.

Another example :

template<class T> class Array
{
    T * _items;

    T & operator [] ( size_t pos ) const
    {
        return _items[ pos ];
    }
}

I can use the const keyword here because the value of _items is not modified (no matter what we do with what it points to). For the compiler this is correct, but if i access one of the items and modify it, the operator [] will allow the modification of the array even though it's supposed to be const, i.e supposed not to modify the " contents " of the array.

What should be done in that kind of case ? Use or don't use the const keyword ?

Thank you :)

like image 399
Virus721 Avatar asked Dec 17 '14 20:12

Virus721


People also ask

What is the point of using const?

The const keyword allows you to specify whether or not a variable is modifiable. You can use const to prevent modifications to variables and const pointers and const references prevent changing the data pointed to (or referenced).

What is const * in C++?

The const keyword specifies that a variable's value is constant and tells the compiler to prevent the programmer from modifying it.


1 Answers

It is a matter of your design decision. Constness is intended to be used as a design concept. It is supposed to help you to implement a higher-level concept of what you consider "modifying" and "non-modifying" access. By properly using const on your class methods you can make the property of being "non-modifiable" to propagate from one object to another through references.

"Referencing other objects" can represent at least two different design relationships:

  • It can be used to implement aggregation, in which case the referee is considered to be an integral part of the referrer. In that case you are typically supposed to be enforcing constness of the access to all parts of the complete object: if the referrer is constant, the referee should also be considered constant. It is your responsibility to enforce the latter by properly const-qualifying your class's interface.

    In order to support that concept you'd normally never attempt to modify the referee inside const methods of the referrer (even though it is formally possible). And the other way around: if some method of referrer modifies the content of referee, you should never declare that method const (even though it is formally possible). Also, const methods of referrer should never return non-constant references to the referee.

    In such cases the outside world is not even supposed to know that the aggregated object is stored by reference. It is a just an implementation detail. For the outside world everything should look as if the aggregated object is a direct immediate member of the referrer.

    That is exactly what you observe in case of std::vector. It is designed that way to make sure that the constness of the entire vector propagates to the constness of the vector elements. In order to achieve that it implements two versions of operator []

    reference       operator[]( size_type pos );
    const_reference operator[]( size_type pos ) const;
    
  • It can be used to implement pure referencing that does not imply aggregation. In that case the referee is considered a completely separate, unrelated object. The constness of the referrer is not supposed to propagate to the referee. It is OK in this case to modify the referee in the const methods of the referrer. It is OK to return non-constant references to the referee from const methods of the referrer.

    An example of this design would be standard smart pointers: a pointer refers to the pointee, but constness of the pointer does not in any way imply constness of the pointee. For example, std::shared_ptr has only one version of operator *

    T& operator*() const;
    

    which is declared const yet returns a non-constant reference to the pointed object.

like image 155
AnT Avatar answered Oct 20 '22 07:10

AnT