Ошибка C2106: '=': левый операнд должен иметь значение l

Глядя на другие вопросы, касающиеся ошибки C2106, я все еще теряюсь в том, что проблема с моим кодом. При компиляции я получаю следующие ошибки:

c: \ driver.cpp (99): ошибка C2106: '=': левый операнд должен иметь значение l

c:\driver.cpp(169): ошибка C2106: '=': левый операнд должен иметь значение l

Строка кода выглядит следующим образом:

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

Я не понимаю, почему возникает эта ошибка. В этом проекте я изменил мой driver.cpp из массива указателей на объекты сотрудников на собственный шаблон Vector, который я создал. Я объявляю Вектор следующим образом...

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

Любая помощь приветствуется...

4 ответа

Эта ошибка выдается по той же причине, по которой вы не можете сделать что-то вроде этого:

36 = 3;

Ваша версия Vector::at должен возвращать ссылку, а не значение.
L-значения называются L-значениями, потому что они могут появляться слева от назначения. R-значения не могут появляться на левой стороне, поэтому мы называем их R-значениями. Вы не можете назначить 3 в 36 так как 36 это не lvalue, это rvalue, временный. У него нет адреса памяти. По той же причине вы не можете назначить NULL в payroll.at(i),


Ваше определение:

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

Что это должно быть:

template<typename V> V& MyVector::at(std::size_t n)
template<typename V> const V& MyVector::at(std::size_t n) const

В сообщении говорится, что вы пытаетесь присвоить выражение, которое не является lvalue. Для встроенных типов вы можете присваивать только lvalue (отсюда и название: lvalue = значение, которое может быть слева от оператора присваивания, а rvalue = значение, которое должно быть справа от оператор присваивания).

Так что же такое lvalue или rvalue? Рассмотрим следующий код:

int a;
a = 3;

В этом назначении a является lvalue (если это не так, компилятор будет жаловаться). То есть выражение a относится к объекту, который может быть изменен. С другой стороны, 3 это значение, то есть в основном значение. Конечно, вы не можете назначить 3; компилятор будет жаловаться на утверждение 3=a; с точно таким же сообщением, которое вы получили в своем коде.

Таким образом, в первом приближении lvalue обозначает объект, а rvalue обозначает значение. Обратите внимание, что это также верно для присвоения формы

a = b;

где b также является переменной. Здесь происходит так называемое преобразование lvalue в rvalue: то, что назначено, не является объектом b, но его текущая стоимость.

Теперь рассмотрим следующий случай:

int f();
f() = 3;

Здесь вы можете утверждать, что функция f возвращает объект (если вы используете какой-то определенный пользователем тип, вы даже можете увидеть его конструкцию / разрушение). Но компилятор все еще жалуется на сообщение, которое вы получили. Зачем?

Ну, даже если вы считаете f чтобы вернуть объект, это временный объект, который немедленно исчезнет. Поэтому присваивать значение не имеет особого смысла, потому что вы все равно ничего не сможете сделать с ним потом.

Поэтому вот второе правило:

Всякий раз, когда есть выражение, которое создает временный объект, C++ определяет это выражение как rvalue.

И теперь мы подходим к определению MyVector::at() который вы не показали, но который, согласно сообщению об ошибке, вероятно, выглядит примерно так:

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

Это имеет по существу ту же форму, что и f выше, так как он также возвращает T (employee* в твоем случае). Вот почему компилятор жалуется.

И эта жалоба полезна: даже если компилятор не будет жаловаться, код не будет соответствовать вашим ожиданиям. return оператор возвращает копию объекта data[i], Таким образом, если утверждение payment.at(i)=NULL; после компиляции произойдет следующее:

  1. Внутренний объект data[i] (или как вы назвали это в своем коде) копируется и временная копия возвращается.
  2. Оператор назначил эту временную копию, но оставляет исходный объект в MyVector без изменений.
  3. Временная копия разрушается, не оставляя следов вашего назначения.

Это почти наверняка не то, что вы хотели. Вы хотели изменить внутренний объект. Для этого вы должны вернуть ссылку на этот объект. Ссылка ссылается на объект, с которым она была инициализирована, вместо того, чтобы делать копию. Соответственно, ссылка, даже если она возвращена, является lvalue (поскольку в C++11 есть второй тип ссылки, который ведет себя по-другому, но нам здесь не нужно об этом заботиться). Ваша исправленная функция затем читает

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

и с этим определением, payment.at(i)=NULL; не только компилирует, но и делает то, что вы хотите: изменить внутренне сохраненные iуказатель в payment в NULL,

Ваша функция MyVector::at(unsigned) вероятно, не правильно объявлен и выглядит так:

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

То, что вы хотите, чтобы это выглядело так:

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

Обратите внимание на ссылочный параметр (&), который вернет любой элемент по ссылке и позволит использовать выражение в качестве l-значения.

Реальный вопрос в том, почему вы не используете std::vector вместо?

Термин l-значение в C++ означает, что "левое значение" имеет неправильный тип. Поскольку вы используете оператор присваивания для него, чтобы быть правильным типом, это должно быть значение, которому может быть присвоено новое значение, что означает, что оно не может быть константой. Скорее всего, ваш звонок payroll.at() возвращает постоянное значение вместо ссылки на фактическое значение. Попытка присвоить ему новое значение вызовет ошибку l-значения.

Другие вопросы по тегам