7

Looking at the other questions regarding error C2106, I am still lost as to what the issue is with my code. While compiling I get the following errors:

c:\driver.cpp(99): error C2106: '=' : left operand must be l-value

c:\driver.cpp(169): error C2106: '=' : left operand must be l-value

The line of code is as follows:

payroll.at(i) = NULL; //Line 99
payroll.at(count++) = ePtr; //Line 169

I am failing to understand why this error is being thrown. In this project I have changed my driver.cpp from an array of employee object pointers to a custom Vector template that I made. I declare the Vector as follows...

//Declare an Vector to hold employee object pointers
MyVector <employee*> payroll;

Any help is appreciated...

Community
  • 1
  • 1
KQball
  • 107
  • 1
  • 1
  • 6
  • 4
    What does MyVector::at return? – tohava Jul 29 '13 at 18:00
  • 5
    MyGuess : `YourVector::at()` must be returning by value which is the problem. – Nawaz Jul 29 '13 at 18:03
  • @tohava this is what it returns... 'return myArray[n];' unless the reference is greater than the vector size then it returns the value that was passed into the function. – KQball Jul 29 '13 at 18:13
  • 1
    @KQball: What is the *return type* (that determines *how* you return *what* you return)? – Nawaz Jul 29 '13 at 18:24

4 Answers4

14

This error is being thrown for the same reason you can't do something like this:

36 = 3;

Your version of Vector::at should be returning a reference rather than a value.
Lvalues are called Lvalues because they can appear on the left of an assignment. Rvalues cannot appear on the left side, which is why we call them rvalues. You can't assign 3 to 36 because 36 is not an lvalue, it is an rvalue, a temporary. It doesn't have a memory address. For the same reason, you cannot assign NULL to payroll.at(i).


Your definition:

template <class V> V MyVector<V>::at(int n)

What it should be:

template<typename V> V& MyVector::at(std::size_t n)
template<typename V> const V& MyVector::at(std::size_t n) const
user123
  • 8,970
  • 2
  • 31
  • 52
  • 2
    Well, I'm looking at the docs and it says it returns a reference.. you sure? – Lews Therin Jul 29 '13 at 18:01
  • 3
    @LewsTherin You are correct, but this is a custom implementation of a vector – user123 Jul 29 '13 at 18:05
  • In assumption of a forward directed memory layout, the paramater of `at` should be `unsigned`. – Pixelchemist Jul 29 '13 at 18:17
  • The parameter of `at` should probably be `size_t`, but that's not really the biggest issue here. – Sven Jul 29 '13 at 18:21
  • I don't mean there's a problem with the answer, I meant that it was besides the point of the original question. Sorry for the confusion. – Sven Jul 29 '13 at 18:26
  • @Sven: Yeah but it says "_What it should be:_" but it is likeley not the case since it **should** be unsigned for what `at` of a vector usually does. – Pixelchemist Jul 29 '13 at 18:47
  • You two are correct. I used `std::size_t` so it's identical to the definition of `std::vector::at` ~ – user123 Jul 29 '13 at 18:52
6

The message says that you try to assign to an expression which is not an lvalue. For built-in types, you can only assign to lvalues (that's where the name comes from: lvalue = value that can be on the left hand side of the assignment operator, while rvalue = value that must be on the right hand side of the assignment operator).

So what is an lvalue or an rvalue? Consider the following code:

int a;
a = 3;

In this assignment a is an lvalue (if it weren't, the compiler would complain). That is, the expression a refers to an object which can be modified. On the other hand, 3 is an rvalue, that is, basically a value. Of course you cannot assign to 3; the compiler would complain about the statement 3=a; with exactly the same message you got in your code.

So as a first approximation, an lvalue designates an object, while an rvalue designates a value. Note that this is also true for assignment of the form

a = b;

where b also is a variable. What happens here is the so-called lvalue to rvalue conversion: What is assigned is not the object b, but its current value.

Now consider the following case:

int f();
f() = 3;

Here you might argue that the function f does return an object (if you use some user-defined type, you even can see its construction/destruction). But the compiler still complains with the message you got. Why?

Well, even if you consider f to return an object, it is a temporary object which will go away immediately. So it does not make much sense to assign a value because you cannot do anything with it anyway afterwards.

Therefore here's the second rule:

Whenever there's an expression which produces a temporary object, C++ defines that expression as rvalue.

And now we come to the definition of MyVector::at() which you did not show, but which, according to the error message, probably looks similar to this:

template<typename T>
 T MyVector<T>::at(int i)
{
  return data[i];
}

This has essentially the same form as f above, as it also returns a T (an employee* in your case). This is why the compiler complains.

And that complaint is helpful: Even if the compiler wouldn't complain, the code would not dio what you almost certainly intended. The return statement returns a copy of the object data[i]. Thus if the statement payment.at(i)=NULL; had compiled, what would actually happen would be the following:

  1. The internal object data[i] (or however you called it in your code) is copied and the temporary copy returned.
  2. The statement assigned that temporary copy, but leaves the original object in MyVector unchanged.
  3. The temporary copy gets destructed, leaving no trace of your assignment.

This is almost certainly not what you wanted. You wanted to change the internal object. To do so, you have to return a reference to that object. A reference refers to the object it was initialized with instead of making a copy. Correspondingly, a reference, even when returned, is an lvalue (since C++11 there's a second type of reference which behaves differently, but we don't need to care about that here). Your corrected function then reads

template<typename T>
 T& MyVector<T>::at(int i)
{
  return data[i];
}

and with that definition, payment.at(i)=NULL; not only compiles, but actually does what you want: Change the internally stored i-th pointer in payment to NULL.

celtschk
  • 19,311
  • 3
  • 39
  • 64
3

Your function MyVector::at(unsigned) is probably not correctly declared and looks like this:

T MyVector::at(unsigned i) { /* implementation detail */ }

What you want is for it to look like this:

T& MyVector::at(unsigned i) { /* implementation detail */ }

Notice the reference parameter (&), which will return whatever element by reference and allow the expression to be used as an l-value.

The real question is why aren't you use std::vector instead?

Silex
  • 1,707
  • 17
  • 24
0

the term l-value in c++ means the 'left-value' is of the improper type. Since you are using the assignment operator on it, to be the proper type it must be a value that can be assigned a new value, which means it can not be a constant. Most likely, your call to payroll.at() is returning a constant value instead of a reference to the actual value. Trying to assign a new value to it will then cause an l-value error.

Lochemage
  • 3,974
  • 11
  • 11