Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const and non-const in stl containers

Tags:

c++

stl

boost

The STL vector template defines element accessors as both const and non-const variants, for example:

reference operator[](size_type __n) 
    {return *(this->_M_impl._M_start + __n);}
const_reference operator[](size_type __n) const 
    {return *(this->_M_impl._M_start + __n);}

When does the compiler decide to use one version over the other? The vector itself is not defined as const, neither are the elements stored in it. So given two functions:

A f(int i) const
    { return myVector[i]; }
A f(int i)
    { return myVector[i]; }

My understanding is that the first would call the const version of operator[] and return a const A. The second calls the non-const version and returns a non-const A?

To me, the first version of f() seems to be the "correct" one to write since the function isn't altering anything, but it is perhaps surprising to the caller that it returns a const A. Surely if I wanted a const A returned, I should need to define f() as:

const A f(int i) const    //Note the extra const at the start
    { return myVector[i]; }

Which would tell whoever is writing the caller to expect a const back.

So the extra const appears by magic? And what does the extra const get applied to if I'm using a boost::ptr_vector rather than std::vector? The data? The pointer? both?

like image 537
Nigel Hawkins Avatar asked Dec 17 '10 19:12

Nigel Hawkins


1 Answers

This takes advantage of a tricky part of the function overload rules. First off, cv-qualifiers after the close parenthesis of a method prototype are just like cv-qualifiers on arguments, but they apply to the implicit this argument. In a hypothetical C++ variant where you had to declare this, the prototypes would be like so:

reference operator[](this_type this, size_type n);
const_reference operator[](const this_type this, size_type n);

Therefore, if the object you are calling the method on is const, the second overload is a closer match and will be called. If it's not, the first overload will be called. So you get a const_reference back if you index a const container, and you get a reference back if you index a non-const container. The const_reference object then enforces the read-only nature of the container it points into.

Sometimes const_reference is the same as const reference and sometimes it isn't; for the more complicated containers, a reference is a class with nontrivial code, and that code has to be different for the read-only variety. The standard consistently uses const_reference so that implementors have freedom to do that when they need to.

like image 76
zwol Avatar answered Sep 22 '22 20:09

zwol