Is it bad practice to return a vector element by reference?
class X{
vector<Y> v;
public:
Y& getRefFromVectorOfY(unsigned int index){
retrun v.at(index);
}
void addToVectorOfY(Y y){
v.push_back(move(y));
}
};
While this is efficient and clean, the issue is that it breaks encapsulation. If v is a vector, and a private member of a class, the caller will now have a ref to an element in the private vector that they can not only read from but also assign to (overwrite).
Can do this
x.getRefFromVectorOfY(0).setNumber(7); //not bad..actually good
or
x.getRefFromVectorOfY(0) = move(Y2); //very bad!
or even
Y3 = move(x.getRefFromVectorOfY(0)); //OMG
Of course, we may return a const & instead, allowing only const operations.
Returning an element by value is less efficient since it is a copy.
If the get method was to move the element out of the vector to return by value, the vector would lose data integrity since it would no longer store the data that was moved out (element would be reset to default state inside the vector... so... still left in the vector but as junk data).
WHY AM I ASKING THIS? THE PERSPECTIVE
If you're following the rule of zero, and you have a private vector member in a class, you need convenience methods to axs and set the vector. This raises the question how to return values.
The problem with return by ref is that it breaks encapsulation unless it is const ref.
So, should I only return a const ref forcing any setting of values to use the setVector method? The problem with this is that sometimes you store custom type...like Y..in the vector and you need to access Y's non-const methods. While a const & protects the vector element from being re-set or overwritten in the vector, it also prevents caller from using the returned elements non-const methods.
So if I only return const &... I can't do
x.getRefFromVectorOfY(0).setNumber(7); //Y has a setNumber method
and that would be a problem. It would be a problem because I don't want to re-implement any of Y's methods in class X. That would be another layer of indirection and a lot of code redundancy. This speaks against having a return a const & general policy. Not practical in many cases.
So I would have to override based on constness of return value, which you can't do.
So I would have to have two get functions with diff names, one returning const &... and other just returning a &. That feels dirty.
const Y& getConstRefFromVectorOfY(unsigned int index){
retrun v.at(index);
}
Y& getRefFromVectorOfY(unsigned int index){
retrun v.at(index);
}
EDIT
I added this table to summarize the issues and the goals.
I want the caller of class X be able to do the following safely and efficiently:
- MOD VECTOR
- MOD ELEMENT IN VECTOR
- READ ELEMENT IN VECTOR
Summery of options as I understand them:
"-" is a con and "+" is a pro.
OPTION 1 -- Return copy of vector element
MOD VECTOR
+ Can mod vector via class method addToVectorOfY
MOD ELEMENT IN VECTOR
- No way to mod Y element itself in vector without
indirection & redundancy (re-implementation of some Y methods)!!!
Because modifying returned copy does not modify what is in vector.
READ ELEMENT IN VECTOR
+ Yes, inefficiently can read element data by looking at copy of element
OPTION 2 -- Return ref of vector element
MOD VECTOR
- Can mod vector in non-obious ways via returned ref to element. Breaks encapsulation.
Dangerous and also redundant because class has setter method to mod vector.
+ Can mod vector via class method addToVectorOfY
MOD ELEMENT IN VECTOR
+ Can call non-const methods of element returned
- Some danger to the reference being invalid upon vector resize
READ ELEMENT IN VECTOR
+ Yes, with maximum efficiency
- Some danger to the reference being invalid upon vector resize
OPTION 3 -- Return const ref of vector element
MOD VECTOR
+ Can mod vector via class method addToVectorOfY
MOD ELEMENT IN VECTOR
- No way to mod Y element itself in vector without
indirection & redundancy (re-implementation of some Y methods)!!!
READ ELEMENT IN VECTOR
+ Yes, with maximum efficiency
- Some danger to the reference being invalid upon vector resize
Is there a way for a user of class X to do all 3 safely and efficiently?