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?
- return a const reference to prevent the function to change the returning value
- 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
Difference Between Logical and Physical Const-Ness
How to Enable_Shared_From_This of Both Parent and Derived
How to Merge Two Bst's Efficiently
C++ Best Way to Get Integer Division and Remainder
Add External Libraries to Cmakelist.Txt C++
Get Size of Terminal Window (Rows/Columns)
Image Edge Smoothing with Opencv
Is There a Compact Equivalent to Python Range() in C++/Stl
Std::Vector Reserve() and Push_Back() Is Faster Than Resize() and Array Index, Why
Should I Use Public or Private Variables
Using Declaration in Variadic Template
What Does an Ampersand After This Assignment Operator Mean
How to Use Dylib in MAC Os X (C++)
Why Sizeof Int Is Wrong, While Sizeof(Int) Is Right