0

Let's say I have this simple container class

class Array{
private:
    int m_l{};
    int *m_d{};


public:
    Array() = default;

    Array(int len)
        : m_l{len}
    {
        assert(len >= 0);
        if (len > 0)
        {
            m_d = new int[len]{};
        }
    }
    ~Array(){
        delete[] m_d;
    }

// -------------------------------------------
    int& operator[](int i){
        assert(i >= 0 && i <= m_l && "Index in operator [] out of range");
        return m_d[i];
    }
//--------------------------------------------
};

Specifically

int& operator[](int i)
{
    assert(i >= 0 && i <= m_l && "Index in operator [] out of range");
    return m_d[i];
}

I have overloaded the [] operator to get the subscript, I am just learning about operator overloading and container classes.

Array arr{5};
arr[0] = 32;
arr[1] = 34;
std::cout << arr[0];

32

The code compiles and executes as expected, but if I remove the & from the function, making it

int operator[](int i)
{
    assert(i >= 0 && i <= m_l && "Index in operator [] out of range");
    return m_d[i];
}

The compiler throws an error

lvalue required as left of operand assignment

Why does this error occur, what is the significance of & in the function?

  • Your constructor does not assign the pointer if the length is 0. The destructor is not checking the length before calling `delete[]`, leading to undefined behavior. – Eljay Oct 01 '20 at 19:54
  • the program will terminate if the length is 0 due to `assert()` –  Oct 01 '20 at 19:57
  • The `assert(len >= 0);` won't terminate with a len of 0. – Eljay Oct 01 '20 at 21:03

2 Answers2

3

Without the reference in the subscript operator overload, you are returning a temporary rvalue from the function. An rvalue cannot occur on the left side of an assignment operation.

Specifically, when you write arr[0], without the reference in your overload that would just return an integer (as whatever the value of arr[0] is).

Array arr{5};
arr[0]; //Without the reference, this returns 5.
5 = 32; //Written out, this is what the expression turns to.

The 5 is an rvalue that cannot be assigned to - what would 5 = 32 mean? With the reference overload, you are returning a reference (the memory location) to the first element of the array which is an lvalue. Reading this might help you understand it better.

NotAProgrammer
  • 552
  • 1
  • 5
  • 15
  • can you explain a little more? as you would to a novice –  Oct 01 '20 at 19:36
  • But if I do `std::cout << arr[0]`, I get the value of `arr[0]` not the memory location ? What is the difference? –  Oct 01 '20 at 20:01
  • 1
    You get the value, yes, but what is important here is that with `int` that value is a temporary object (a copy) that ceases to exist as soon as it exits it scope. This object has a memory location that is **completely unrelated** to `m_d[i]` that you return from your underlying int array. If you return a reference, you are actually returning the object that you initially put into your array. – NotAProgrammer Oct 01 '20 at 20:19
  • Yes I got that part, I am confused as to why I get `5` if I do `std::cout << arr[0]` and not something like `0x61fe1c` (an address). What I mean is if I have a variable `a` , if I do `std::cout << &a` I get the hex number, but if I have a function that returns a reference to a like `int& s() {return a;}` `std::cout << s()` gives `5` but not the hex –  Oct 01 '20 at 20:25
  • Because underneath you are using the [array subscript operator](https://en.cppreference.com/w/cpp/container/array/operator_at) which returns a reference (as a value). When I said `memory location` in my post, I meant that the object that you are returning is at the same memory location as in the array, not that the actual value will be the memory address. – NotAProgrammer Oct 01 '20 at 20:30
  • I think that is the part i don't understand, returning a reference - as a value –  Oct 01 '20 at 20:42
  • The value is the reference. `int&` version returns an `int` that refers to the value at `m_d[i]`, i.e. `&(m_d[i]) == &(arr[0])`. – NotAProgrammer Oct 01 '20 at 21:03
1

The & in int& means that your operator[] returns a reference (ie, an alias, usually implemented as a memory address) to an int variable. Once bound to a variable, any value that is assigned to a reference gets assigned to the variable it refers to.

Without the &, your operator[] would return a copy (ie, the value) of the int instead.

When a function returns a value rather than a reference, the compiler creates a temporary variable to hold that value. That temporary exists only until the end of the statement that creates it. That temporary variable is known as an rvalue, ie a value that is used on the right-hand side of an assignment. To preserve the value longer, the caller must assign it to another variable.

Things like named variables, references to variables, etc are lvalues, ie they can used on the left-hand side of an assignment. An rvalue cannot be used on the left-hand side, only on the right-hand side.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770