2

I'm doing the exercises from "Programming Principles and Practice using C++" and I found a class with this member function here:

const vector<string> &get_name() const { return name; }

where name is a vector: vector< string> name;

The book presents 2 concepts so far (up to page 235):

  1. return a const reference to prevent the function to change the returning value:

    const vector<string> &get_name() { return name; } 
    
  2. a const member function that cannot modify the object. in this case, it would be like this:

    vector<string> get_name() const { return name; } 
    

Maybe I'm not getting this completely, but aren't those 2 concepts the same? Not wanting to change the "name" vector.

Why the need for both "const"?

Thanks to anyone taking the time to reply!

Abhishek Bhagate
  • 5,583
  • 3
  • 15
  • 32
Theodore
  • 179
  • 12
  • 4
    They aren't the same because the second version has to make a (potentially expensive) copy of `name`. – François Andrieux Jun 17 '20 at 18:59
  • First `const` apply to returned object only, and the method cannot be called on const instance/object. the second one apply to instance/object, so both const/non-const instance can call that method. – Jarod42 Jun 17 '20 at 19:03
  • The first says "you're not allowed to change this thing that you're asking for, and I might change while you ask", the second says "I'm not going to change if you ask for this thing, and you can do whatever you want with the thing I give you". Those are not the same concept. – molbdnilo Jun 17 '20 at 19:30

3 Answers3

3
  1. return a const reference to prevent the function to change the returning value
  2. a const member function that cannot modify the object. in this case, it would be like this

This could have been a bit more clearer, I'll try my shot at explaining it better.

Returning a const reference prevent the returned object to be mutated by callers.

Here's an example:

// let get_name be `const std::vector<std::string>& get_name()`

np1.get_name().size(); // ok, size is a const function of vector
np1.get_name().push_back("hello"); // ERROR! push_back is not a const function of vector

So indeed, a caller cannot change the name vector. The return type is const qualified.

However, if the function get_name itself is not const qualified (not the return type), then it is allowed to change name from the class itself.

You see, member functions receive a hidden this parameter, which is a pointer to the object being called on. The pointer can either point to a const object, or a mutable object. Here's the comparison:

// returning const ref, callers cannot change the name vector
const std::vector<std::string>& get_name() {
    // The function receive a `this` that points to a mutable,
    // we can change the name from the inside
    this->name.push_back("another");
    return name;
}

// the `this` pointer points to const -----v---v
const std::vector<std::string>& get_name() const {
    this->name.push_back("another"); // ERROR! Cannot mutate member of const object
    return name;
}

Const-qualified member function are really useful for the caller, as it knows that whenever this function is called, its state won't change.

For example, not only you know that the vector::size() function won't mutate the vector, but the compiler guarantees it since it's a const qualified member function.

And for the last bit, the code you posted here:

vector<string> get_name() const { return name; } 

This will not return a reference, but will make a copy. The caller can mutate the copy however it wants, but cannot mutate name itself.

Here's an example of a copy mutated:

auto name_copy = np1.get_name();

name_copy.push_back("another name"); // works, we mutated the copy by adding an element
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • Thanks a lot, Guillaume! it really helps. If I may ask another question, how do you change the copy for the last line: vector get_name() const { return name; } – Theodore Jun 18 '20 at 14:11
  • could you please take a look at my code here? https://github.com/ltr01/PPPCpp-Tests/blob/master/9.3_test2.cpp I defined the functions get_name1(), get_name2() and get_name3() like in your examples. And the functions 1 and 2 behave just like you said. But after creating the object np3 and adding another element to it, I still cannot see that new element. I'm doing something wrong or I still don't get something ? Thanks a lot for taking the time for this! It really helps. – Theodore Jun 18 '20 at 16:54
  • 1
    @Theodore You won't be able to mutate `name` using `get_name3()`. Since you return a copy, any mutation you do such as `push_back` will be done on the copy, not the original `name` object. If you want mutable access, use `std::vector& get_name()`. Since the returned reference is non const, you will be able to mutate the referenced object. – Guillaume Racicot Jun 18 '20 at 18:06
  • I finally succeeded to push_back on a copy, inside the function get_name3(). That's what I couldn't do. And I get what you mean by mutating the referenced object. WOW I understand so much more since 24h ago! Thanks a lot, Guillaume! – Theodore Jun 18 '20 at 19:31
1

A member-function has an implicit this-reference.

And the referenced object can be const-qualified, allowing the member-function to be called on a constant object, by following the parameter-list in the declaration with const.

Whether that member-function also returns a reference to some constant data may be strongly correlated, but is still completely the programmers decision.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
1

This case return a const reference to vector<string>, and we cannot change this vector. But we can change vector<string> name in this function.

const vector<string> &get_name() { return name; } 

In this case return a copy of vector<string>, and we can change this vector, because it's already another vector. In this function we cannot change vector<string> name.

vector<string> get_name() const { return name; } 
Anton Shwarts
  • 515
  • 2
  • 10