Returning Non-Const Reference from a Const Member Function

Returning non-const reference from a const member function

int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad

In a const-function, every data member becomes const in such way that it cannot be modified. int becomes const int, int * becomes int * const, and so on.

Since the type of a in your first function becomes int * const, as opposed to const int *, so you can change the data (which is modifiable):

  m.geta() = 5;  //works, as the data is modifiable

Difference between : const int* and int * const.

  • const int* means the pointer is non-const, but the data the pointer points to is const.
  • int * const means the pointer is const, but the data the pointer points to is non-const.

Your second function tries to return const int &, since the type of b become const int. But you've mentioned the actual return type in your code as int &, so this function would not even compile (see this), irrespective of what you do in main(), because the return type doesn't match. Here is the fix:

 const int & getb(void) const { return b; }  

Now it compiles fine!.

Is it bad practice to return a non const reference to an element of a private vector member?

Is it bad practice to return a vector element by reference?

Yes, in general? In most cases, I would think if you took the time to encapsulate a member variable of a class as private in order to control the access and manipulation of that variable, designing a member function that will easily break that control renders the first step moot. This may not always be true depending on the use case, but here you pose the problem so abstractly that it is hard to give a specific answer. The only real problem I can identify in your post is this:

Returning an element by value is less efficient and since it is a copy, it is less efficient.

I guess the real question to ask here is there a meaningful, measurable performance difference between maintaining greater access control to the member variable versus having more direct access to the underlying memory so you can manipulate it faster? You are right that the return by reference is more efficient in some ways, but does that actually make a practical difference in your particular code?

Additionally, it also matters what level of data integrity you need to maintain for the private member variables you are exposing. einpoklum makes a great point that many standard containers follow this paradigm. They have no expectations on the values that are stored in the container, only that they maintain control over allocation/deletion of the memory held by them. Your class may have a stronger control requirements about what values the member values take. For example, if all the data elements in that vector needed to be non-negative, then by exposing a reference to that memory you lose the ability to have the class make those kind of data integrity guarantees. It really just depends on the requirements, although I prefer the paradigm of selectively releasing control over a member variable as needed rather than giving full access and slowly taking it away when you want to add additional guarantees.

Const method returning non-const reference compiles

In short, it's your responsibility.

In const member function, only the data member itself becomes const. For arr (which is supposed to be of type int*) it'll become int * const (i.e. const pointer), not int const * (i.e. pointer to const); i.e. the pointer becomes const but the pointee doesn't. So technically it's possible to return a non-const reference to the pointee, even it might not make much sense in fact.

You'd better to overload on operator[], like most STL containers do. e.g.

// const version
int const & Vector::operator[](const int index) const
{
return arr[index];
}

// non-const version
int & Vector::operator[](const int index)
{
return arr[index];
}

Why the need for both const reference and const member function?

  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

Const method returning non-const reference to vector element

You could use const_cast, and it'll do the work here, (according to the usage posted). But you won't need it for this case (and for most cases).

You could (and should) add non-const member function overloading for it. Then const member function returns reference to const, non-const member function returns reference to non-const. The appropriate one will be invoked via overload resolution.

const T& x() const
{
return myVec[0];
}
T& x()
{
return myVec[0];
}

Why can't a const method return a non-const reference?

The idea behind the const member function is you should be able to call them on const objects. const functions can't modify the object.

Say you have a class

class A
{
int data;
void foo() const
{
}
};

and on object and a function call:

A const a;
a.foo();

Inside A::foo, this->data is treated as if its type is int const, not int. Hence, you are not able to modify this->data in A:foo().

Coming to your example, the type of this->ranks in getRanks() is to be considered as const vector<int> and not vector<int>. Since, auto conversion of const vector<int> to vector<int>& is not allowed, the compiler complains when you define the function as:

vector<int>& getRanks()const{
return ranks;
}

It won't complain if you define the function as:

const vector<int>& getRanks()const{
return ranks;
}

since const vector<int> can be auto converted to const vector<int>&.

Return 'this' as non-const from const member function

The reason why this fails is that inside a const member function, this is really a const Point*, not a Point*. Thus you are trying to initialize a non-const reference from a const pointer. It's not that the compiler isn't believing you, you're just asking for two incompatible things at one time.

This is one of the very few valid uses of const_cast, in my opinion. Normally, using const_cast is almost always a sign of a design error, or worse a programming error.

Here, the function is really const and should be const, but there is no reason why you shouldn't be able to chain something non-const afterwards, so it's arguably legitimate to do such a thing.

Do note, however, although the function is strictly const (in respect to the object, not so much in its use of IO functions!), one thing you should consider is that in some (rare) cases, it may result in code that doesn't do what you want. The compiler is allowed to cache the result of a const function and omit another call to the same const function (since you promised that it won't change anything). Therefore, it is allowable to optimize some_point.Print().Print(); into some_point.Print(). This is probably not a problem for you (why would you want to print the same values twice), just something to be generally aware of.

Returning a non-const reference causes a binding reference error

If you are inside a const method, all the data members are considered const.

That is, inside this function:

std::weak_ptr<Node> &getPrev() const

you can imagine the member variables like this:

const T data;
const std::weak_ptr<Node> prev;
const std::shared_ptr<Node> next;

It should be clear that you cannot return a non-const reference to a const object:

const int x;

int& getX()
{
return x; // error
}

The reference would allow you to modify x even though it is const, so this is forbidden (formally: a non-const reference cannot bind to a const object).

Inside a const member function of Node, prev is a const std::weak_ptr<Node>, so a std::weak_ptr<Node>& cannot bind to it for the exact same reason.


It appears that within insert you do intend to modify node (by changing its prev value), in which case the getPrev function should not be const (because you intend to modify the object). But this kind of access should probably be reserved for the DoubleLinkedList and not some arbitrary outside user. It then becomes a question of interface design: Which parts of your code are implementation details and how those should be hidden from users? Which parts are the interface that users should interact with (with minimal opportunity for breaking things)?

Can const member function return a non-const pointer to a data member?

A const member function can only return a const pointer or reference to a member.

However, your example isn't returning a pointer to a member; it's returning a copy of a member that happens to be a pointer. That is allowed in a const member function (even if the pointer happens to point to another member).

This would not be allowed (note that it's now returning a reference):

int *& foo() const {return _px;}

but this would (returning a const reference):

int * const & foo() const {return _px;}


Related Topics



Leave a reply



Submit