Operator [ ] overloading with const and non const versions - c++

I started learning C++ not long ago and I've got an issue.
I know that a lot of similar questions has been asked about this topic.
However, I didn't deeply understand something which is why I am here.
So, my question is:
why do I need to provide 2 versions of the [] operator?
isn't the const version sufficient?
For example, I have been working on an Array class: (The last 2 operators are the relevant ones)
class Array
{
private:
int* _arrPtr;
int _len;
public:
friend ostream& operator<<(ostream& out, const Array& other);
friend istream& operator>>(istream& in, Array& other);
Array(int len = 10, int val = 0);
Array(const Array& other);
~Array();
void setArray(int len);
int* getArray() const;
void setLen(int len) { this->_len = len; }
int getLen() const { return this->_len; }
Array operator+(const Array& other) const;
Array& operator=(const Array& other);
Array operator+(int val);
int& operator[](int index) const;
int& operator[](int index);
};
can I replace the two operators
int& operator[](int index) const;
int& operator[](int index);
only with the const one?
int& operator[](int index) const;
Wouldn't it be the same??? wouldn't it be the same for any operator overloading if the there is only one const version of the operator?(under the assumption that all of the const methods do have the word 'const' at the end of their declaration)
Thank you very much!!!

Whether or not you need two overloads depends on what interface you want to provide.
If you want to be able to modify elements even in const instances of Array, then yes, the single int& operator[](int index) const; is enough. This is what std::unique_ptr does.
But if you want elements of const Arrays to be read-only, and at the same time elements of non-const Arrays to be mutable, you need two overloads. std::vector does that.
Usually the second option is preferred.

You cannot write int& operator[](int index) const;. Instead, you need to write:
const int& operator[](int index) const;
int& operator[](int index);
Why is this so? Because if something is const, you can have only const references to it or it's elements, as non-const referenes whould let you change it, which you have no right to.
If you want const references when the thing is const and non-const references in case the thing is not const, you need the overloading. If you only want const references in any case, you may get without the overloading.

Related

How to overload the [] operator to support both assignment and const parameters in other functions?

Consider a class SomeClass:
class SomeClass{
public:
// Constructors, other members
float& operator[](const unsigned i);
friend bool operator==(const SomeClass &A, const SomeClass &B);
};
Suppose this is how the == operator is overloaded for this class (not the actual implementation, but an overly simplified version):
bool operator==(const SomeClass &A, const SomeClass &B){
if (A[0] == B[0])
return true;
return false;
}
This would throw a compiler error, since the overloaded [] operator requires the instance to be non-const. However, if I change the definition of the [] operator to allow for const instances, I can no longer do assignment:
// ASSUMING: const float& operator[](const unsigned i) const;
SomeClass a;
a[0] = 0; // error, because the return value of [] is a reference to const!
I really don't want to drop the const in the parameters of the == operator, since the operands don't change within the function. What is the right way to deal with this issue?
Overload operator [] to provide both:
float& operator [](unsigned int i);
float operator [](unsigned int i) const;
For a generic T that’s not cheap to copy, use a T const& return value. The general pattern for implementing read/write operator [] is
T& operator [](index_type i);
T const& operator [](index_type i) const;

subscript operators of matrix for getting and assigning value

I have a matrix of bool values
class BoolMatix
{
};
I would like to implement 2 subscript operators that can do this
BoolMatix b(...);
b[5]=true;
and
bool val=GetTruth(5);
GetTruth(5) will return b[5]
I have tried this
const bool operator[](int index);
but this seems to work with GetTruth(index n) only, how about the assignment ?
You have to return a reference
bool& BoolMatrix::operator [](int index);
const bool operator[](int index);
You return a const bool with this, so you just cannot assign something to it

overloading operator. Const arguments make some problems

class Vector{
......
.......
private:
int dim;
public:
int getDim() {
return this->dim;
}
const Vector operator+(const Vector& right){
this->getSize();
}
};
And I got compile error in this->getSize();. It is caused fact, that argument right is const. I don't know where is problem. I don't try modify right.
Presumably you have a non-const method Vector::getSize(). You need to make it const so that it can be called on const objects or via const references or pointers to const. For example:
int getSize() const;
^^^^^
Also note that it doesn't make much sense to return a const value (and would inhibit move semantics if you had them). The canonical form of an addition member operator would be
// const method: A = B + C should not modify B
Vector operator+(const Vector& right) const;
^^^^^
and the non-member
Vector operator+(const Vector& left, const Vector& right);

Vector, proxy class and dot operator in C++

A question related to a custom Vector class in C++.
template <typename T>
class Vector
{ ...
private:
T * mData; int mSize;
public:
proxy_element operator[](const size_type index) { return proxy_element(*this, index); }
const T& operator[](const size_type index) const { return mData[index]; }
};
template <typename T>
class proxy_element
{ ...
proxy_element(Vector<T>& m_parent, const size_type index);
proxy_elem& operator=(const T& rhs); // modifies data so invalidate on other memories
bool operator==(const proxy_elem& rhs) // only read, just copy data back.
...
}
The reason for using proxy_element class is to distinguish and optimize read and writes operations, considering that the vector data can reside in GPU device memories as well. So any read operation require only to copy latest data back (if any) but a readwrite/write operation require invalidating data in device memories.
This design work well when the element type is primitive. However for more complex element types, there is one issue:
struct person{ int age; double salary; };
int main()
{
Vector<person> v1(10);
v[1].age = 10; // gives error as operator[] returns proxy_element for which "." operator has no meaning
}
AFAIK, the "." operator cannot be overload in C++. One obvious solution is to not use proxy_elem and just return regular reference (T &), assuming that each access is a write access, but that will be inefficient for obvious reasons.
Is there any other work around which gives me "." operator working while retaining ability to distinguish between read and write operations?
One option is to make such data types immutable (private member variables, initialised by a constructor, and the only setter is the class's assignment operator). This way, the only means to change anything is to assign to an entire instance of the class, which can be channeled through a proxy_element.
Marcelo Cantos's answer is, of course, the proper way to do things. However, there is the complicated and crazy workaround of specialization. (Not recommended.)
//if it's a class, inherit from it to get public members
template<class T>
class proxy_element : public T {
...
proxy_element(Vector<T>& m_parent, const size_type index);
proxy_elem& operator=(const T& rhs); // modifies data so invalidate on other memories
bool operator==(const proxy_elem& rhs) // only read, just copy data back.
...
};
//pretend to be a pointer
template<>
class proxy_element<T*> {
...
proxy_element(Vector<T>& m_parent, const size_type index);
proxy_elem& operator=(const T& rhs); // modifies data so invalidate on other memories
bool operator==(const proxy_elem& rhs) // only read, just copy data back.
...
};
//otherwise, pretend to be primitive
#define primitive_proxy(T) \
template<> class proxy_element {
...
proxy_element(Vector<T>& m_parent, const size_type index);
proxy_elem& operator=(const T& rhs); // modifies data so invalidate on other memories
bool operator==(const proxy_elem& rhs) // only read, just copy data back.
...
};
primitive_proxy(char)
primitive_proxy(unsigned char)
primitive_proxy(signed char) //this is distinct from char remember
primitive_proxy(short)
primitive_proxy(unsigned short)
primitive_proxy(int)
primitive_proxy(unsigned int)
primitive_proxy(long)
primitive_proxy(unsigned long)
primitive_proxy(long long)
primitive_proxy(unsigned long long)
primitive_proxy(char16_t) //if GCC
primitive_proxy(char32_t) //if GCC
primitive_proxy(wchar_t)
primitive_proxy(float)
primitive_proxy(double)
primitive_proxy(long double)

Inlining an overloaded operator c++

Can/should an overloaded operator have to be inlined to gain better efficiency (wrt time or whatever) if that operator will have to be used frequently?
I want to overload the '+' operator to add big vectors very frequently in my code. Hence the question.
Ideally, you'd profile your code and then decide what to inline. There really isn't much of a difference between when you decide to inline regular operators, over overloaded ones.
If you are adding big vectors, then the overhead of a function call to the plus will be small relative to the time to actually add the two vectors. So marking the operator+ inline is not likely to improve your overall run time.
Let the compiler to decide about optimization.
The keyword inline is misleading: the compiler can -in fact- always do what it needs, just like with the old auto (do you remenber those days?) and register.
It's modern meaning is "defined in header: discard if not used, merge if seen more times".
The compiler should inline smallish functions for you automatically in release builds.
Much more important is to define a move constructor and move assignment. If your arrays are very large and you're doing multiple operations at the same time, you can also use expression classes to improve execution speed.
template <class left, class right>
struct AddExpr {
const left& _left;
const right& _right;
AddExpr(const left& Left, const right& Right)
:_left(Left), _right(Right)
{assert(left.count() == right.count());}
int count() const {return _left.count();}
int operator[](int index) const {return _left[i]+_right[i];}
};
class Array {
int* data;
int size;
int count() const {return size;}
Array& operator=(AddExpr expr) {
for(int i=0; i<expr.count(); ++i)
data[i] = expr[i];
};
AddExpr operator+(const Array& lhs, const Array& rhs)
{return AddExpr<Array, Array>(lhs, rhs);}
AddExpr operator+(const Array& lhs, const Expr& rhs)
{return AddExpr<Array, Expr>(lhs, rhs);}
AddExpr operator+(const Expr& lhs, const Array& rhs)
{return AddExpr<Expr, Array>(lhs, rhs);}
AddExpr operator+(const Expr& lhs, const Expr& rhs)
{return AddExpr<Expr, Expr>(lhs, rhs);}
int main() {
Array a, b, c, d;
Array c = (a+b) + (c+d); //awesome on lines like this
}
This removes all the temporary objects, and greatly improves cache efficiency. But I've completely forgotten what this technique is called.