What follows is a very simplified discussion, totally avoiding the words “lvalue” and “rvalue”.
Any
object may be
const (immutable) or non-const (mutable). We tend to start out thinking of our variables as mutable:
1 2 3 4 5
|
int x = 12;
std::string s = "Hello";
x = -7; // change x
s += " world!"; // change s
|
However, sometimes we get access to objects that we either:
• are not permitted to change, or
• wish to promise the caller that we will not change.
1 2 3
|
const char* s = "Hello world!"; // This string is 'const' because the data is in memory I am not allowed to change
bool f( const std::string& s ); // This function promises not to change my string
|
This part about references is usefully important. There are two kinds of reference: direct (or true) references, which C++ simply calls “references”, and
indirect references, which we all “pointers”.
A pointer is an indirect reference because the pointer itself is not a reference. It is a value that can be used to obtain a (direct) reference. This process is called “dereferencing”.
In any case, this duality means that there are
two things that can be const or non-const:
• the pointer object itself
• the target object
The type of a pointer’s target is listed
before the asterisk. The pointer object itself is listed
after the asterisk. Hence:
1 2 3 4 5
|
int * p; // mutable target, mutable pointer (1)
const int * p; // const target, mutable pointer (2)
int const * p; // const target, mutable pointer (2)
int * const p; // mutable target, const pointer (3)
const int * const p; // const target, const pointer (4)
|
So:
(1) I can change targets, and I can modify the target.
(2) I can change targets, but I cannot modify the target.
(3) I cannot change targets, but I can modify the target.
(4) I can neither change targets nor modify the target.
Types (1) and (2) pointers are most common, because it is the constness (or mutability) of the target that we are typically most interested in.
You have touched on a different issue about pointers and references. When dealing with large objects, we typically want to save time and memory by not duplicating them whenever possible. Hence, we will simply pass a reference (direct or indirect) to avoid doing so. Neither constness nor mutability are necessary to do so.
Hopefully this will give you a little better context for your answer.
Your contact list can be mutable — you may add, change, or remove numbers at any time.
Your contact list can be const — the contact list is not allowed to change.
An immutable contact list is not very useful, so, you don’t really have any motive to do that.
You
can, however, write a function that promises not to change the list in order to look up a number:
bool is_number_in_contacts( const Vector<int> & contacts, int number );
Hope this helps.