Error lvalue required as left operand of assignment перевод

Привет, Хабр! Представляю вашему вниманию перевод статьи Eli Bendersky, Understanding of lvalues and rvalues in C and C++. От переводчика: предлагаю Вашему вни...

Время прочтения
9 мин

Просмотры 142K

Привет, Хабр! Представляю вашему вниманию перевод статьи Eli Bendersky, Understanding of lvalues and rvalues in C and C++.

От переводчика: предлагаю Вашему вниманию перевод интересной статьи об lvalue и rvalue в языках C/C++. Тема не нова, но знать об этих понятиях никогда не поздно. Статья рассчитана на новичков, либо на программистов переходящих с C (или других языков) на C++. Поэтому будьте готовы к подробному разжёвыванию. Если вам интересно, добро пожаловать под кат

Термины lvalue и rvalue не являются чем-то таким, с чем часто приходится сталкиваться при программировании на C/C++, а при встрече не сразу становится ясным, что именно они означают. Наиболее вероятное место столкнуться с ними — это сообщения компилятора. Например, при компиляции следующего кода компилятором gcc:

int foo() { return 2; }
int main()
{
    foo() = 2;
    return 0;
}

Вы уведите нечто следующее:

test.c: In function 'main':
test.c:8:5: error: lvalue required as left operand of assignment

Согласен, что этот код немного надуманный, и вряд ли Вы будете писать нечто подобное, однако сообщение об ошибке упоминает lvalue — термин, который не так часто увидишь в туториалах по C/C++. Другой пример нагляден при компиляции следующего кода при помощи g++:

int& foo()
{
    return 2;
}

Вы увидите следующую ошибку:

testcpp.cpp: In function 'int& foo()':
testcpp.cpp:5:12: error: invalid initialization of non-const reference
of type 'int&' from an rvalue of type 'int'

Опять же, в сообщение об ошибке упоминается мистическое rvalue. Что же в C и C++ понимается под lvalue и rvalue? Это и есть тема данной статьи.

Простое определение

Для начала нарочито дадим определения lvalue и rvalue в упрощённой форме. В дальнейшем эти понятия будут рассмотрены под увеличительным стеклом.

lvalue (locator value) представляет собой объект, который занимает идентифицируемое место в памяти (например, имеет адрес).

rvalue определено путём исключения, говоря, что любое выражение является либо lvalue, либо rvalue. Таким образом из определения lvalue следует, что rvalue — это выражение, которое не представляет собой объект, который занимает идентифицируемое место в памяти.

Элементарные примеры

Термины, определённые выше, могут показаться немного нечёткими. Поэтому стоит сразу рассмотреть несколько простых поясняющих примеров. Предположим, мы имеем дело с переменной целого типа:

int var;
var = 4;

Оператор присваивания ожидает lvalue с левой стороны, и var является lvalue, потому что это объект с идентифицируемым местом в памяти. С другой стороны, следующие заклинания приведут к ошибкам:

4 = var;       // ERROR!
(var + 1) = 4; // ERROR!

Ни константа 4, ни выражение var + 1 не являются lvalue
(что автоматически их делает rvalue). Они не lvalue, потому что оба являются временным результатом выражений, которые не имеют определённого места в памяти (то есть они могут находится в каких-нибудь временных регистрах на время вычислений). Таким образом, присваивание в данном случае не несёт в себе никакого семантического смысла. Иными словами — некуда присваивать.

Теперь должно быть понятно, что означает сообщение об ошибке в первом фрагменте кода. foo возвращает временное значение, которое является rvalue. Попытка присваивания является ошибкой. То есть, видя код foo() = 2;, компилятор сообщает, что ожидает lvalue с левой стороны оператора присваивания.

Однако, не все присваивания результату вызова функции ошибочны. Например, использование ссылок в C++ делает это возможным:

int globalvar = 20;

int& foo()
{
    return globalvar;
}

int main()
{
    foo() = 10;
    return 0;
}

Здесь foo возвращает ссылку, которая является lvalue, то есть ей можно придать значение. Вообще, в C++ возможность возвращать lvalue, как результат вызова функции, существенна для реализации некоторых перегруженных операторов. Как пример приведём перегрузку оператора [] в классах, которые реализуют доступ по результатам поиска. Например std::map:

std::map<int, float> mymap;
mymap[10] = 5.6;

Присваивание mymap[10] работает, потому что неконстантная перегрузка std::map::operator[] возвращает ссылку, которой может быть присвоено значение.

Изменяемые lvalue

Изначально, когда понятие lvalue было введено в C, оно буквально означало «выражение, применимое с левой стороны оператора присваивания». Однако позже, когда ISO C добавило ключевое слово const, это определение нужно было доработать. Действительно:

const int a = 10; // 'a' - lvalue
a = 10;           // но ему не может быть присвоено значение!  

Таким образом не всем lvalue можно присвоить значение. Те, которым можно, называются изменяемые lvalue (modifiable lvalues). Формально C99 стандарт определяет изменяемые lvalue как:

[…] lvalue, тип которого не является массивом, не является неполным, не имеет спецификатор const, не является структурой или объединением, содержащими поля (также включая поля, рекурсивно вложенные в содержащиеся агрегаты и объединения) со спецификатором const.

Преобразования между lvalue и rvalue

Образно говоря, конструкции языка, оперирующие значениями объектов, требуют rvalue в качестве аргументов. Например, бинарный оператор ‘+’ принимает два rvalue в качестве аргументов и возвращает также rvalue:

int a = 1;        // a - lvalue
int b = 2;        // b - lvalue
int c = a + b;    // '+' требует rvalue, поэтому a и b конвертируются в rvalue 
                  // и rvalue возвращается в качестве результата

Как мы уже видели раньше, a и b оба lvalue. Поэтому в третьей строке они подвергаются неявному преобразованию lvalue-в-rvalue. Все lvalue, которые не являются массивом, функцией и не имеют неполный тип, могут быть преобразованы в rvalue.

Что насчёт преобразования в другую сторону? Можно ли преобразовать rvalue в lvalue? Конечно нет! Это бы нарушило суть lvalue, согласно его определению (Отсутствие неявного преобразования означает, что rvalue не могут быть использованы там, где ожидается lvalue).

Это не означает, что lvalue не могут быть получены из rvalue явным способом. Например, унарный оператор ‘*’ (разыменование) принимает rvalue в качестве аргумента, но возвращает lvalue в качестве результата. Рассмотрим следующий верный код:

int arr[] = {1, 2};
int* p = &arr[0];
*(p + 1) = 10;   // OK: p + 1 rvalue, однако *(p + 1) уже lvalue

Обратно, унарный оператор ‘&’ (адрес) принимает lvalue как аргумент и производит rvalue:

int var = 10;
int* bad_addr = &(var + 1);  // ОШИБКА: требуется lvalue для унарного оператора '&'
int* addr = &var;            // ОК: var - lvalue
&var = 40;                   // ОШИБКА: требуется lvalue с левой стороны
                             //         оператора присваивания

Символ «&» играет несколько другую роль в C++ — он позволяет определить ссылочный тип. Его называют «ссылкой на lvalue». Неконстантной ссылке на lvalue не может быть присвоено rvalue, так как это потребовало бы неверное rvalue-в-lvalue преобразование:

std::string& sref = std::string();  // ОШИБКА: неверная инициализация             
                                    // неконстантной ссылки типа 'std::string&'
                                    // rvalue типа 'std::string'

Константным ссылкам на lvalue можно присвоить rvalue. Так как они константы, значение не может быть изменено по ссылке и поэтому проблема модификации rvalue просто отсутствует. Это свойство делает возможным одну из основополагающих идиом C++ — допуск значений по константной ссылке в качестве аргументов функций, что позволяет избежать необязательного копирования и создания временных объектов.

CV-специфицированные rvalues

Если прочесть внимательно часть С++ стандарта, касающуюся преобразования lvalue-в-rvalue (глава 4.1 в драфте стандарта C++11), то можно увидеть следующее:

lvalue (3.10) на тип T, не являющимся функциональным, или массивом, может быть преобразован в rvalue. […] Если T не класс, типом rvalue является cv-неспецифицированная версия типа T. Иначе, типом rvalue является T.

Так что же значит «cv-неспецифицированный»? CV-спецификатор — это термин, используемый для описания const и volatile спецификаторов типа.

Из главы 3.9.3:

Каждый тип, который является cv-неспецифицированным полным или неполным объектным типом или типом void (3.9), имеет соответственно три cv-специфицированные версии: тип со спецификатором const, тип со спецификатором volatile и тип со спецификаторами const volatile. […] CV-специфицированные и cv-неспецифицированные типы являются различными, однако они имеют одинаковое представление и требования по выравниванию.

Но как всё это связано с rvalue? В языке C rvalue никогда не имеют cv-специфицированных типов. Это свойство lvalue. Однако в C++ классовые rvalue могут быть cv-специфицированным, что не касается встроенных типов вроде int. Рассмотрим пример:

#include <iostream>

class A {
public:
    void foo() const { std::cout << "A::foo() constn"; }
    void foo() { std::cout << "A::foo()n"; }
};

A bar() { return A(); }
const A cbar() { return A(); }


int main()
{
    bar().foo();  // вызовет foo
    cbar().foo(); // вызовет foo const
}

Вторая строчка в функции main вызовет метод foo() const, так как cbar возвращает объект типа const A, который отличен от A. Это как раз то, что имелось в виду в последнем предложении выдержки из стандарта выше. Кстати, заметьте, что возвращаемое cbar значение является rvalue. Это был пример cv-специфицированных rvalue в действии.

Ссылки на rvalue (C++11)

Ссылки на rvalue и сопутствующий концепт семантики переноса являются одним из наиболее мощным инструментом, добавленным в язык C++11. Подробная дискуссия на эту тему выходит за рамки этой скромной статьи (вы можете найти кучу материала, просто погуглив «rvalue references». Вот некоторые ресурсы, которые я нахожу полезными: этот, этот и особенно вот этот), но всё же я хотел бы привести простой пример, потому что считаю, что данная глава является наиболее подходящим место, чтобы продемонстрировать как понимание lvalue и rvalue расширяет наши возможности рассуждать о нетривиальных концепциях языка.

Добрая половина статьи была потрачена на объяснение того, что одним из самых главных различий между lvalue и rvalue является тот факт, что lvalue можно изменять, в то время как rvalue — нет. Что же, C++11 добавляет одну важнейшую характерную особенность в этом различии, разрешая нам иметь ссылки на rvalue и тем самым изменять их в некоторых случаях.

Как пример рассмотрим наипростейшую реализацию динамического массива целых чисел. Давайте посмотрим лишь на методы относящиесе к теме данной главы:

class Intvec
{
public:
    explicit Intvec(size_t num = 0)
        : m_size(num), m_data(new int[m_size])
    {
        log("constructor");
    }

    ~Intvec()
    {
        log("destructor");
        if (m_data) {
            delete[] m_data;
            m_data = 0;
        }
    }

    Intvec(const Intvec& other)
        : m_size(other.m_size), m_data(new int[m_size])
    {
        log("copy constructor");
        for (size_t i = 0; i < m_size; ++i)
            m_data[i] = other.m_data[i];
    }

    Intvec& operator=(const Intvec& other)
    {
        log("copy assignment operator");
        Intvec tmp(other);
        std::swap(m_size, tmp.m_size);
        std::swap(m_data, tmp.m_data);
        return *this;
    }
private:
    void log(const char* msg)
    {
        cout << "[" << this << "] " << msg << "n";
    }

    size_t m_size;
    int* m_data;
};

Итак, здесь присутствуют обычные конструктор и деструктор, конструктор копирования и оператор присваивания (это каноническая реализация копирующего оператора присваивания с точки зрения усточивости к исключениям. Используя конструктор копирования и затем не выбрасывающий исключения std::swap, мы можем быть уверены, что не может возникнуть промежуточного состояния с непроинициализированной памятью, если где-то произойдёт исключение). Все они используют функцию логирования, чтобы мы могли понять, когда они на самом деле вызваны.

Давайте запустим простой код, который копирует содержимое v1 в v2:

Intvec v1(20);
Intvec v2;

cout << "assigning lvalue...n";
v2 = v1;
cout << "ended assigning lvalue...n";

И вот, что мы увидим:

assigning lvalue...
[0x28fef8] copy assignment operator
[0x28fec8] copy constructor
[0x28fec8] destructor
ended assigning lvalue...

Что совершенно логично, так как это точно отражает, что происходит внутри оператора присваивания. Но давайте предположим, что мы хотим присвоить v2 некоторое rvalue:

cout << "assigning rvalue...n";
v2 = Intvec(33);
cout << "ended assigning rvalue...n";

Хотя здесь я только присваиваю значение свеже созданному вектору, это является одной из демонстраций общего случая, когда некоторое временное rvalue создаётся и присваивается v2 (это может случится например, если функция возвращает вектор). Вот что мы увидим на экране:

assigning rvalue...
[0x28ff08] constructor
[0x28fef8] copy assignment operator
[0x28fec8] copy constructor
[0x28fec8] destructor
[0x28ff08] destructor
ended assigning rvalue...

Ого! Выглядит очень хлопотно. В частности, здесь потребовалась дополнительная пара вызовов конструктора с деструктором, чтобы создать, а потом удалить временный объект. И это печально, так как внутри копирующего оператора присваивания другой временный объект создаётся и удаляется. Дополнительная работа за зря.

Но, нет! C++11 даёт нам ссылки на rvalue, с помощью которых можно реализовать «семантику переноса», а в частности «переносящий оператор присваивания» (теперь понятно почему я всё время называл operator= копирующим оператором присваивания. В C++11 эта разница становится важной). Давайте добавим другой operator= в IntVec:

Intvec& operator=(Intvec&& other)
{
    log("move assignment operator");
    std::swap(m_size, other.m_size);
    std::swap(m_data, other.m_data);
    return *this;
}

Двойной асперсанд — это ссылка на rvalue. Он означает как раз то, что и обещает — даёт ссылку на rvalue, который будет уничтожен после вызова. Мы можем использовать этот факт, чтобы просто «стащить» внутренности rvalue — они ему всё равно не нужны! Вот, что выведется на экран:

assigning rvalue...
[0x28ff08] constructor
[0x28fef8] move assignment operator
[0x28ff08] destructor
ended assigning rvalue...

Как мы видим, вызывается новый переносящий оператор присваивания, так как rvalue присваивается v2. Вызовы конструктора и деструктора всё же необходимы для временного объекта, который создаётся через Intvec(33). Однако другой временный объект внутри оператора присваивания больше не нужен. Оператор просто меняет внутренний буфер rvalue со своим, и таким образом деструктор rvalue удаляет буфер самого объекта, который больше не будет использоваться. Чисто!

Хочу только отметить ещё раз, что этот пример только вершина айсберга семантики переноса и ссылок на rvalue. Как вы можете догадаться, это сложная тема с множеством частных случаев и загадок. Я пытался лишь продемонстрировать очень интересное применение различий между lvalue и rvalue в C++. Компилятор очевидно может их различать и позаботится о вызове правильного конструктора во время компиляции.

Заключение

Можно написать много C++ кода, не задумываясь о разногласиях rvalue и lvalue, опуская их как непонятный жаргон компилятора в сообщениях об ошибках. Однако, как я пытался показать в этой статье, лучшее владение этой темы обеспечит более глубокое понимание определённых конструкций C++, и сделает части стандарта C++ и дискуссии между экспертами языка для вас более доступными.

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

You need to compare, not assign:

if (strcmp("hello", "hello") == 0)
                             ^

Because you want to check if the result of strcmp("hello", "hello") equals to 0.

About the error:

lvalue required as left operand of
assignment

lvalue means an assignable value (variable), and in assignment the left value to the = has to be lvalue (pretty clear).

Both function results and constants are not assignable (rvalues), so they are rvalues. so the order doesn’t matter and if you forget to use == you will get this error. (edit:)I consider it a good practice in comparison to put the constant in the left side, so if you write = instead of ==, you will get a compilation error. for example:

int a = 5;
if (a = 0) // Always evaluated as false, no error.
{
    //...
}

vs.

int a = 5;
if (0 = a) // Generates compilation error, you cannot assign a to 0 (rvalue)
{
    //...
}

(see first answer to this question: https://stackoverflow.com/questions/2349378/new-programming-jargon-you-coined)

You need to compare, not assign:

if (strcmp("hello", "hello") == 0)
                             ^

Because you want to check if the result of strcmp("hello", "hello") equals to 0.

About the error:

lvalue required as left operand of
assignment

lvalue means an assignable value (variable), and in assignment the left value to the = has to be lvalue (pretty clear).

Both function results and constants are not assignable (rvalues), so they are rvalues. so the order doesn’t matter and if you forget to use == you will get this error. (edit:)I consider it a good practice in comparison to put the constant in the left side, so if you write = instead of ==, you will get a compilation error. for example:

int a = 5;
if (a = 0) // Always evaluated as false, no error.
{
    //...
}

vs.

int a = 5;
if (0 = a) // Generates compilation error, you cannot assign a to 0 (rvalue)
{
    //...
}

(see first answer to this question: https://stackoverflow.com/questions/2349378/new-programming-jargon-you-coined)

Unicorn_666

0 / 0 / 1

Регистрация: 18.03.2017

Сообщений: 34

1

19.03.2017, 10:06. Показов 14142. Ответов 4

Метки нет (Все метки)


Вот сам код, ошибку выдает на строчках 90-92.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include<iostream>
using namespace std;
 
 
class Array{
 
protected:
    int* arr;
    int sz;
public:
    Array(){
 
        arr = new int (1);
 
    }
    Array(int n){
 
        arr = new int(n);
        sz = n;
 
    }
 
    ~Array(){
 
        delete[] arr;
 
    }
 
    Array(const Array &other){
 
        arr = new int(other.sz);
        for(int i = 0; i < sz; i++)
            arr[i] = other[i];
 
    }
 
    int operator [] (int pos) const {
        if (pos >= 0 && pos < sz)
            return arr[pos];
        else
            return -1;
 
    }
 
    void push(int el){
 
        arr[++sz - 1] = el;
 
    }
 
 
};
 
 
class ReversedArray:
    public Array {
 
public:
 
    ReversedArray(int n){
 
        arr = new int(n);
        sz = n;
 
    }
 
    ~ReversedArray(){
 
        delete[] arr;
 
    }
 
    int operator [] (int pos){
 
        if (pos >= 0 && pos < sz)
            return arr[sz - pos - 1];
        else
            return -1;
 
    }
 
 
};
 
 
int main(){
 
    ReversedArray *p2 = new ReversedArray(3);
    Array *p = p2;
    (*p)[0] = 3;
    (*p)[1] = 5;
    (*p)[2] = -1;
    for (int i = 0; i < 3; i++)
        cout << (*p2)[i];
 
}

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь



0



Вездепух

Эксперт CЭксперт С++

10435 / 5704 / 1553

Регистрация: 18.10.2014

Сообщений: 14,100

19.03.2017, 10:33

2

Лучший ответ Сообщение было отмечено Unicorn_666 как решение

Решение

Цитата
Сообщение от Unicorn_666
Посмотреть сообщение

ошибку выдает на строчках 90-92

А как еще? Ваш оператор [] возвращает значение типа int. Вы в это значение что-то пытаетесь присваивать. Это как это?

Это примерно как написать strlen(s) = 42 или 3 + 5 = 10. Получите ту же ошибку.



1



nd2

3433 / 2812 / 1249

Регистрация: 29.01.2016

Сообщений: 9,426

19.03.2017, 10:35

3

Цитата
Сообщение от Unicorn_666
Посмотреть сообщение

C++
1
arr = new int(n);
C++
1
arr = new int[n];



0



0 / 0 / 1

Регистрация: 18.03.2017

Сообщений: 34

19.03.2017, 11:00

 [ТС]

4

А надо сделать так, чтоб возвращал ссылку, да?



0



Вездепух

Эксперт CЭксперт С++

10435 / 5704 / 1553

Регистрация: 18.10.2014

Сообщений: 14,100

19.03.2017, 11:07

5

Цитата
Сообщение от Unicorn_666
Посмотреть сообщение

А надо сделать так, чтоб возвращал ссылку, да?

Да, надо. Но тогда return -1; в случае выхода за пределы делать не получится.



0



IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

19.03.2017, 11:07

5

Ошибка компиляции кода Arduino: «lvalue требуется как левый операнд присваивания»

Я получаю эту ошибку, когда пытаюсь скомпилировать свой код:

lvalue требуется как левый операнд присваивания.

Код считывается кнопками через аналоговый порт. Вот где ошибка (в void (loop)):

while (count < 5){
    buttonPushed(analogPin) = tmp;

        for (j = 0; j < 5; j++) {
                while (tmp == 0) { tmp = buttonPushed(analogPin); }                 //something wrong with the first half of this line!

        if(sequence[j] == tmp){
                        count ++;
                }

        else { 
            lcd.setCursor(0, 1); lcd.print("Wrong! Next round:");                       delay(1000);
                        goto breakLoops;
                }

        }
}

breakLoops:
elapsedTime = millis() - startTime;

На самом верху у меня: int tmp;

3 ответы

buttonPushed(analogPin) = tmp;

Эта линия не работает. buttonPushed является функцией и может читать только из analogPin; вы не можете присвоить результат функции в C. Я не уверен, что вы пытаетесь сделать, но я думаю, что вы, вероятно, хотели вместо этого использовать другую переменную.

ответ дан 25 мар ’12, в 15:03

У вас есть такая строка:

     buttonPushed(analogPin) = tmp;

Вместо этого вы можете захотеть:

     tmp = buttonPushed(analogPin);

С оператором присваивания объект слева от = оператор получает значение справа от = оператор, а не наоборот.

ответ дан 25 мар ’12, в 15:03

Проблема здесь в том, что вы пытаетесь назначить временное значение / rvalue. Для присвоения в C требуется lvalue. Я предполагаю подпись твоей buttonPushed функция по существу следующая

int buttonPushed(int pin);

Здесь buttonPushed функция возвращает копию найденной кнопки, которую не имеет смысла назначать. Чтобы вернуть фактическую кнопку вместо копии, вам нужно использовать указатель.

int* buttonPushed(int pin);

Теперь вы можете сделать свой код назначения следующим

int* pTemp = buttonPushed(analogPin);
*pTemp = tmp;

Здесь присвоение находится в месте, которое является lvalue и будет законным.

ответ дан 25 мар ’12, в 16:03

Не тот ответ, который вы ищете? Просмотрите другие вопросы с метками

c
loops
while-loop
arduino

or задайте свой вопрос.

  1. while ((getMeasurement()>40)||(digitalRead(A1)=HIGH) ||(digitalRead(A2)=HIGH))
    вот на что ругается IDE.
    lvalue required as left operand of assignment-вот такая вот ошибка.
    Помогите пожалуйста.

  2. (digitalRead(A1)=HIGH)-он ругается не на чтение цифрового сигнала с аналога!!!
    Тестил- все пашет

  3. Правильнее читать «присвоить значение результату, возвращаемому функцией», а этот самый результат является константой, что и не нравится компилятору :)
    Присвоить функции — это digitalRead = 5;

    Кстати, погуглите «указатель на функцию» ради интереса — весьма забавный фокус ;)

  4. Мой косяк, ошибся, 2 часа ночи и все такое :)

    Последнее редактирование: 13 окт 2013

  5. #include <Strela.h>

    #define butt S1
    #define sleep 125

    byte lamps[] = {L4, L3, L2, L1};

    unsigned long last_press = 0;

    boolean butt_flag = LOW;
    boolean lamp_flag = LOW;

    int high_lamp = 0;

    void setup() {
    for (int i = 0; i <= 3; i++) {
    uPinMode(lamps, OUTPUT);
    }

    uPinMode(butt, INPUT);
    }

    void loop() {
    if (uDigitalRead(butt) && butt_flag == LOW && millis() — last_press > 200) {
    butt_flag = HIGH;
    lamp_flag = !lamp_flag;
    last_press = millis();
    }

    if (uDigitalRead(butt) == LOW && butt_flag) butt_flag = LOW;

    if (lamp_flag) {
    uDigitalWrite(lamps[high_lamp], HIGH);
    delay(sleep);
    uDigitalWrite(lamps[high_lamp], LOW);
    high_lamp++;

    if(high_lamp > 3) high_lamp = 0;
    }
    }

    Я как-то в этом скетче тоже с равенствами накосячил.

    Вложения:

  6. #include <Strela.h>

    #define butt  S1
    #define sleep 125

    byte lamps[] = {L4, L3, L2, L1};

    unsigned long last_press = 0;

    boolean butt_flag = LOW;
    boolean lamp_flag = LOW;

    int high_lamp = 0;

    void setup() {
      for (int i = 0; i <= 3; i++) {
        uPinMode(lamps[i], OUTPUT);
      }

      uPinMode(butt, INPUT);
    }

    void loop() {
      if (uDigitalRead(butt) && butt_flag == LOW && millis() last_press > 200) {
        butt_flag = HIGH;
        lamp_flag = !lamp_flag;
        last_press = millis();
      }

      if (uDigitalRead(butt) == LOW && butt_flag) butt_flag = LOW;

      if (lamp_flag) {
        uDigitalWrite(lamps[high_lamp], HIGH);
        delay(sleep);
        uDigitalWrite(lamps[high_lamp], LOW);
        high_lamp++;

        if(high_lamp > 3) high_lamp = 0;
      }
    }

    Вложения:

How to fix error lvalue required as left operand of assignmentThe error code that reads lvalue required as left operand of assignment occurs in C-like languages. It happens when the language can not deduce if the left-hand side of a statement is an lvalue or not, thereby causing a frustrating error code to appear.

Keep on reading this guide as our experts teach you the different scenarios that can cause this error and how you can fix it. In addition, we’ll also answer some commonly-asked questions about the lvalue required error.

Contents

  • Why Do You Have the Error Lvalue Required as Left Operand of Assignment?
    • – Misuse of the Assignment Operator
    • – Mishandling of the Multiplication Assignment Operator
    • – Misunderstanding the Ternary Operator
    • – Using Pre-increment on a Temporary Variable
    • – Direct Decrement of a Numeric Literal
    • – Using a Pointer to a Variable Value
    • – Wrong Placement of Expression
  • How To Fix Lvalue Required as Left Operand of Assignment
    • – Use Equality Operator During Comparisons
    • – Use a Counter Variable as the Value of the Multiplication Assignment Operator
    • – Use the Ternary Operator the Right Way
    • – Don’t Pre-increment a Temporary Variable
    • – Use Pointer on a Variable, Not Its Value
    • – Place an Expression on the Right Side
  • Lvalue Required: Common Questions Answered
    • – What Is Meant by Left Operand of Assignment?
    • – What Is Lvalues and Rvalues?
    • – What Is Lvalue in Arduino?
  • Conclusion

Why Do You Have the Error Lvalue Required as Left Operand of Assignment?

The reason you are seeing the lvalue required error is because of several reasons such as: misuse of the assignment operator, mishandling of the multiplication assignment operator, misunderstanding the ternary operator, or using pre-increment on a temporary variable. Direct decrement of a numeric literal, using a pointer to a numeric value, and wrong placement of expression can also cause this error.

The first step to take after receiving such an error is to diagnose the root cause of the problem. As you can see, there are a lot of possible causes as to why this is occurring, so we’ll take a closer look at all of them to see which one fits your experience.

– Misuse of the Assignment Operator

When you misuse the equal sign in your code, you’ll run into the lvalue required error. This occurs when you are trying to check the equality of two variables, so during the check, you might have used the equal sign instead of an equality check. As a result, when you compile the code, you’ll get the lvalue required error.

In the C code below, we aim to check for equality between zero and the result of the remainder between 26 and 13. However, in the check, we used the equal sign on the left hand of the statement. As a result, we get an error when we compare it with the right hand operand.

#include <stdio.h>
// This code will produce an error
int main() {
int a = 26;
int b = 13;
if (a % b = 0) {
printf(“%s”, “It’s all good”);
}
}

– Mishandling of the Multiplication Assignment Operator

Mishandling of the multiplication assignment operator will result in the lvalue required error. This happens if you don’t know how compilers evaluate a multiplication assignment operator. For example, you could write your statement using the following format:

variable * increment = variable

The format of this statement will cause an error because the left side is an expression, and you cannot use an expression as an lvalue. This is synonymous to 20 multiplied by 20 is equal to 20, so the code below is an assignment error.

#include <stdio.h>
int findFactorial(int n) {
int result = 1, i;
for ( i = 1; i <= n; i++) {
result*1 = result; // Here is the error
}
return result;
}
int main() {
int n = 5;
int factorial = findFactorial(n);
printf(“%d”, factorial);
return 0;
}

– Misunderstanding the Ternary Operator

The ternary operator produces a result, it does not assign a value. So an attempt to assign a value will result in an error. Observe the following ternary operator:

(x>y)?y=x:y=y

Many compilers will parse this as the following:

((x>y)?y=x:y)=y

This is an error. That is because the initial ternary operator ((x>y)?y=x:y) results in an expression. That’s what we’ve done in the next code block. As a result, it leads to the error lvalue required as left operand of assignment ternary operator.

#include <stdio.h>
int main() {
int x = 23, y;
x >= 23? y = 4: y = 12;
printf(“%d”, y);
return 0;
}

– Using Pre-increment on a Temporary Variable

An attempt to pre-increment a temporary variable will also lead to an lvalue required error. That is because a temporary variable has a short lifespan, so they tend to hold data that you’ll soon discard. Therefore, trying to increment such a variable will lead to an error.

For example, in the code below, we pre-increment a variable after we decrement it. As a result, it leads to the error lvalue required as increment operand.

#include <stdio.h>
int main() {
int i = 5;
printf(“%dn” ++(-i)); // Error
}

– Direct Decrement of a Numeric Literal

Direct decrement of a numeric literal will lead to an error. That is because in programming, it’s illegal to decrement a numeric literal, so don’t do it, use variables instead. We’ll show you a code example so you’ll know what to avoid in your code.

In our next code block, we aim to reduce the value of X and Y variables. However, decrementing the variables directly leads to error lvalue required as decrement operand. That’s because the direct decrement is illegal, so, it’s an assignment error.

#include <stdio.h>
int main() {
// This is illegal, don’t try this
int x, y;
x = -4–4;
y = -4–(-4);
printf(“x=%d y=%d”, x, y);
}

– Using a Pointer to a Variable Value

An attempt to use a pointer on a variable value will lead to lvalue required error. That’s because the purpose of the pointer sign (&) is to refer to the address of a variable, not the value of the variable.

In the code below, we’ve used the pointer sign (&) on the value of the Z variable. As a result, when you compile the code you get the error lvalue required as unary ‘&’ operand.

#include <stdio.h>
int main() {
int *p;
int z = 5;
p = &5; // Here is the error
return 0;
}

– Wrong Placement of Expression

If you place an expression in the wrong place, it’ll lead to an error lvalue required as operand. It gets confusing if you assign the expression to a variable used in the expression. Therefore, this can result in you assigning the variable to itself.

The following C++ code example will result in an error. That is because we’ve incremented the variable and we assign it to itself.

#include <iostream>
using namespace std;
int main() {
int y[3] = {3,4,5};
int *z=y;
z + 1 = z; // Error
cout << z;
return 0;
}

You can fix the lvalue required error by using equality operator during comparisons, using a counter variable as the value of the multiplication assignment operator, or using the ternary operator the right way. Not pre-incrementing a temporary variable, using pointer on a variable, not its value, and placing an expression on the right side will also help you fix this error.

Now that you know what is causing this error to appear, it’s now time to take actionable steps to fix the problem. In this section, we’ll be discussing these solutions in more detail.

– Use Equality Operator During Comparisons

During comparisons, use the equality operator after the left operand. By doing this, you’ve made it clear to the compiler that you are doing comparisons, so this will prevent an error.

The following code is the correct version of the first example in the code. We’ve added a comment to the corrected code so you can observe the difference.

#include <stdio.h>
int main() {
int a = 26;
int b = 13;
if (a % b == 0) { // Here is the correction
printf(“%s”, “It’s all good”);
}
}

– Use a Counter Variable as the Value of the Multiplication Assignment Operator

When doing computation with the multiplication assignment operator (*=), use the counter variable. Do not use another variable that can lead to an error. For example, in the code below, we’ve made changes to the second example in this article. This shows you how to prevent the error.

#include <stdio.h>
int findFactorial(int n) {
int result = 1, i;
for ( i = 1; i <=n; i++) {
result *= i; // Here is the correct part
}
return result;
}
int main() {
int n = 5;
int factorial = findFactorial(n);
printf(“%d”, factorial);
return 0;
}

– Use the Ternary Operator the Right Way

Use the ternary operator without assuming what should happen. Be explicit and use the values that will allow the ternary operator to evaluate. We present an example below.

#include <stdio.h>
int main() {
int x = 23, y;
y = x >= 23? 4: 12; // The correct ternary operation
printf(“%d”, y);
return 0;
}

– Don’t Pre-increment a Temporary Variable

That’s right, do not pre-increment a temporary variable. That’s because they only serve a temporary purpose.

At one point in this article, we showed you a code example where we use a pre-increment on a temporary variable. However, in the code below, we rewrote the code to prevent the error.

#include <stdio.h>
int main() {
int i = 5;
printf(“%dn”, (–i) * -1); // This should work as expected
}

– Use Pointer on a Variable, Not Its Value

The job of a pointer is to point to a variable location in memory, so you can make an assumption that using the variable value should work. However, it won’t work, as we’ve shown you earlier in the guide.

In the code below, we’ve placed the pointer sign (&) between the left operand and the right operand.

#include <stdio.h>
int main() {
int *p;
int z = 5;
p = &z; // This is right
printf(“%d”, p);
return 0;
}

– Place an Expression on the Right Side

When you place an expression in place of the left operand, you’ll receive an error, so it’s best to place the expression on the right side. Meanwhile, on the right, you can place the variable that gets the result of the expression.

In the following code, we have an example from earlier in the article. However, we’ve moved the expression to the right where it occupied the left operand position before.

#include <iostream>
using namespace std;
int main() {
int y[3] = {3,4,5};
int *z=y;
z = z + 1; // The correct form of assignment
cout << z;
return 0;
}

Lvalue Required: Common Questions Answered

In this section, we’ll answer questions related to the lvalue error and we’ll aim to clear further doubts you have about this error.

– What Is Meant by Left Operand of Assignment?

The left operand meaning is as follows: a modifiable value that is on the left side of an expression.

– What Is Lvalues and Rvalues?

An lvalue is a variable or an object that you can use after an expression, while an rvalue is a temporary value. As a result, once the expression is done with it, it does not persist afterward.

– What Is Lvalue in Arduino?

Lvalue in Arduino is the same as value in C, because you can use the C programming language in Arduino. However, misuse or an error could produce an error that reads error: lvalue required as left operand of assignment #define high 0x1.

What’s more, Communication Access Programming Language (CAPL) will not allow the wrong use of an lvalue. As a result, any misuse will lead to the left value required capl error. As a final note, when doing network programming in C, be wary of casting a left operand as this could lead to lvalue required as left operand of assignment struct error.

Conclusion

This article explained the different situations that will cause an lvalue error, and we also learned about the steps we can take to fix it. We covered a lot, and the following are the main points you should hold on to:

  • A misuse of the assignment operator will lead to a lvalue error, and using the equality operator after the left operand will fix this issue.
  • Using a pointer on the variable instead of the value will prevent the lvalue assignment error.
  • A pre-increment on a temporary variable will cause the lvalue error. Do not pre-increment on a temporary variable to fix this error.
  • An lvalue is a variable while an rvalue is a temporary variable.
  • Place an expression in the right position to prevent an lvalue error, as the wrong placement of expressions can also cause this error to appear.

Error lvalue required as left operand of assignmentYou don’t need to worry when you encounter this lvalue error as you are now well-prepared to handle the problem. Do share our guide when this topic arises in your tech conversations!

  • Author
  • Recent Posts

Position is Everything

Position Is Everything: Your Go-To Resource for Learn & Build: CSS,JavaScript,HTML,PHP,C++ and MYSQL.

Position is Everything

Содержание

  1. Solve error: lvalue required as left operand of assignment
  2. Solve error: lvalue required as left operand of assignment
  3. Some Precautions To Avoid This Error
  4. Понимание lvalue и rvalue в C и С++
  5. Простое определение
  6. Элементарные примеры
  7. Изменяемые lvalue
  8. Преобразования между lvalue и rvalue
  9. CV-специфицированные rvalues
  10. Ссылки на rvalue (C++11)
  11. Заключение

Solve error: lvalue required as left operand of assignment

In this tutorial you will know about one of the most occurred error in C and C++ programming, i.e. lvalue required as left operand of assignment.

lvalue means left side value. Particularly it is left side value of an assignment operator.

rvalue means right side value. Particularly it is right side value or expression of an assignment operator.

Example:

In above example a is lvalue and b + 5 is rvalue.

In C language lvalue appears mainly at four cases as mentioned below:

  1. Left of assignment operator.
  2. Left of member access (dot) operator (for structure and unions).
  3. Right of address-of operator (except for register and bit field lvalue).
  4. As operand to pre/post increment or decrement for integer lvalues including Boolean and enums.

Solve error: lvalue required as left operand of assignment

Now let see some cases where this error occur with code.

Example 1:

When you will try to run above code, you will get following error.

Solution: In if condition change assignment operator to comparison operator, as shown below.

Example 2:

Above code will show the error: lvalue required as left operand of assignment operator.

Here problem occurred due to wrong handling of short hand operator (*=) in findFact() function.

Solution: Just by changing the line ans*i=ans to ans*=i we can avoid that error. Here short hand operator expands like this, ans=ans*i. Here left side some variable is there to store result. But in our program ans*i is at left hand side. It’s an expression which produces some result. While using assignment operator we can’t use an expression as lvalue.

The correct code is shown below.

Example 3:

Above code will show the same lvalue required error.

Reason and Solution: Ternary operator produces some result, it never assign values inside operation. It is same as a function which has return type. So there should be something to be assigned but unlike inside operator.

The correct code is given below.

Some Precautions To Avoid This Error

There are no particular precautions for this. Just look into your code where problem occurred, like some above cases and modify the code according to that.

Mostly 90% of this error occurs when we do mistake in comparison and assignment operations. When using pointers also we should careful about this error. And there are some rare reasons like short hand operators and ternary operators like above mentioned. We can easily rectify this error by finding the line number in compiler, where it shows error: lvalue required as left operand of assignment.

Programming Assignment Help on Assigncode.com, that provides homework ecxellence in every technical assignment.

Comment below if you have any queries related to above tutorial.

Источник

Понимание lvalue и rvalue в C и С++

Привет, Хабр! Представляю вашему вниманию перевод статьи Eli Bendersky, Understanding of lvalues and rvalues in C and C++.

От переводчика: предлагаю Вашему вниманию перевод интересной статьи об lvalue и rvalue в языках C/C++. Тема не нова, но знать об этих понятиях никогда не поздно. Статья рассчитана на новичков, либо на программистов переходящих с C (или других языков) на C++. Поэтому будьте готовы к подробному разжёвыванию. Если вам интересно, добро пожаловать под кат

Термины lvalue и rvalue не являются чем-то таким, с чем часто приходится сталкиваться при программировании на C/C++, а при встрече не сразу становится ясным, что именно они означают. Наиболее вероятное место столкнуться с ними — это сообщения компилятора. Например, при компиляции следующего кода компилятором gcc :

Вы уведите нечто следующее:

Согласен, что этот код немного надуманный, и вряд ли Вы будете писать нечто подобное, однако сообщение об ошибке упоминает lvalue — термин, который не так часто увидишь в туториалах по C/C++. Другой пример нагляден при компиляции следующего кода при помощи g++ :

Вы увидите следующую ошибку:

Опять же, в сообщение об ошибке упоминается мистическое rvalue. Что же в C и C++ понимается под lvalue и rvalue? Это и есть тема данной статьи.

Простое определение

Для начала нарочито дадим определения lvalue и rvalue в упрощённой форме. В дальнейшем эти понятия будут рассмотрены под увеличительным стеклом.

lvalue (locator value) представляет собой объект, который занимает идентифицируемое место в памяти (например, имеет адрес).

rvalue определено путём исключения, говоря, что любое выражение является либо lvalue, либо rvalue. Таким образом из определения lvalue следует, что rvalue — это выражение, которое не представляет собой объект, который занимает идентифицируемое место в памяти.

Элементарные примеры

Термины, определённые выше, могут показаться немного нечёткими. Поэтому стоит сразу рассмотреть несколько простых поясняющих примеров. Предположим, мы имеем дело с переменной целого типа:

Оператор присваивания ожидает lvalue с левой стороны, и var является lvalue, потому что это объект с идентифицируемым местом в памяти. С другой стороны, следующие заклинания приведут к ошибкам:

Ни константа 4 , ни выражение var + 1 не являются lvalue
(что автоматически их делает rvalue). Они не lvalue, потому что оба являются временным результатом выражений, которые не имеют определённого места в памяти (то есть они могут находится в каких-нибудь временных регистрах на время вычислений). Таким образом, присваивание в данном случае не несёт в себе никакого семантического смысла. Иными словами — некуда присваивать.

Теперь должно быть понятно, что означает сообщение об ошибке в первом фрагменте кода. foo возвращает временное значение, которое является rvalue. Попытка присваивания является ошибкой. То есть, видя код foo() = 2; , компилятор сообщает, что ожидает lvalue с левой стороны оператора присваивания.

Однако, не все присваивания результату вызова функции ошибочны. Например, использование ссылок в C++ делает это возможным:

Здесь foo возвращает ссылку, которая является lvalue, то есть ей можно придать значение. Вообще, в C++ возможность возвращать lvalue, как результат вызова функции, существенна для реализации некоторых перегруженных операторов. Как пример приведём перегрузку оператора [] в классах, которые реализуют доступ по результатам поиска. Например std::map :

Присваивание mymap[10] работает, потому что неконстантная перегрузка std::map::operator[] возвращает ссылку, которой может быть присвоено значение.

Изменяемые lvalue

Изначально, когда понятие lvalue было введено в C, оно буквально означало «выражение, применимое с левой стороны оператора присваивания». Однако позже, когда ISO C добавило ключевое слово const , это определение нужно было доработать. Действительно:

Таким образом не всем lvalue можно присвоить значение. Те, которым можно, называются изменяемые lvalue (modifiable lvalues). Формально C99 стандарт определяет изменяемые lvalue как:

[. ] lvalue, тип которого не является массивом, не является неполным, не имеет спецификатор const , не является структурой или объединением, содержащими поля (также включая поля, рекурсивно вложенные в содержащиеся агрегаты и объединения) со спецификатором const .

Преобразования между lvalue и rvalue

Образно говоря, конструкции языка, оперирующие значениями объектов, требуют rvalue в качестве аргументов. Например, бинарный оператор ‘+’ принимает два rvalue в качестве аргументов и возвращает также rvalue:

Как мы уже видели раньше, a и b оба lvalue. Поэтому в третьей строке они подвергаются неявному преобразованию lvalue-в-rvalue. Все lvalue, которые не являются массивом, функцией и не имеют неполный тип, могут быть преобразованы в rvalue.

Что насчёт преобразования в другую сторону? Можно ли преобразовать rvalue в lvalue? Конечно нет! Это бы нарушило суть lvalue, согласно его определению (Отсутствие неявного преобразования означает, что rvalue не могут быть использованы там, где ожидается lvalue).

Это не означает, что lvalue не могут быть получены из rvalue явным способом. Например, унарный оператор ‘*’ (разыменование) принимает rvalue в качестве аргумента, но возвращает lvalue в качестве результата. Рассмотрим следующий верный код:

Обратно, унарный оператор ‘&’ (адрес) принимает lvalue как аргумент и производит rvalue:

Символ «&» играет несколько другую роль в C++ — он позволяет определить ссылочный тип. Его называют «ссылкой на lvalue». Неконстантной ссылке на lvalue не может быть присвоено rvalue, так как это потребовало бы неверное rvalue-в-lvalue преобразование:

Константным ссылкам на lvalue можно присвоить rvalue. Так как они константы, значение не может быть изменено по ссылке и поэтому проблема модификации rvalue просто отсутствует. Это свойство делает возможным одну из основополагающих идиом C++ — допуск значений по константной ссылке в качестве аргументов функций, что позволяет избежать необязательного копирования и создания временных объектов.

CV-специфицированные rvalues

lvalue (3.10) на тип T, не являющимся функциональным, или массивом, может быть преобразован в rvalue. [. ] Если T не класс, типом rvalue является cv-неспецифицированная версия типа T. Иначе, типом rvalue является T.

Так что же значит «cv-неспецифицированный»? CV-спецификатор — это термин, используемый для описания const и volatile спецификаторов типа.

Каждый тип, который является cv-неспецифицированным полным или неполным объектным типом или типом void (3.9), имеет соответственно три cv-специфицированные версии: тип со спецификатором const, тип со спецификатором volatile и тип со спецификаторами const volatile. [. ] CV-специфицированные и cv-неспецифицированные типы являются различными, однако они имеют одинаковое представление и требования по выравниванию.

Но как всё это связано с rvalue? В языке C rvalue никогда не имеют cv-специфицированных типов. Это свойство lvalue. Однако в C++ классовые rvalue могут быть cv-специфицированным, что не касается встроенных типов вроде int . Рассмотрим пример:

Вторая строчка в функции main вызовет метод foo() const , так как cbar возвращает объект типа const A , который отличен от A . Это как раз то, что имелось в виду в последнем предложении выдержки из стандарта выше. Кстати, заметьте, что возвращаемое cbar значение является rvalue. Это был пример cv-специфицированных rvalue в действии.

Ссылки на rvalue (C++11)

Ссылки на rvalue и сопутствующий концепт семантики переноса являются одним из наиболее мощным инструментом, добавленным в язык C++11. Подробная дискуссия на эту тему выходит за рамки этой скромной статьи (вы можете найти кучу материала, просто погуглив «rvalue references». Вот некоторые ресурсы, которые я нахожу полезными: этот, этот и особенно вот этот), но всё же я хотел бы привести простой пример, потому что считаю, что данная глава является наиболее подходящим место, чтобы продемонстрировать как понимание lvalue и rvalue расширяет наши возможности рассуждать о нетривиальных концепциях языка.

Добрая половина статьи была потрачена на объяснение того, что одним из самых главных различий между lvalue и rvalue является тот факт, что lvalue можно изменять, в то время как rvalue — нет. Что же, C++11 добавляет одну важнейшую характерную особенность в этом различии, разрешая нам иметь ссылки на rvalue и тем самым изменять их в некоторых случаях.

Как пример рассмотрим наипростейшую реализацию динамического массива целых чисел. Давайте посмотрим лишь на методы относящиесе к теме данной главы:

Итак, здесь присутствуют обычные конструктор и деструктор, конструктор копирования и оператор присваивания (это каноническая реализация копирующего оператора присваивания с точки зрения усточивости к исключениям. Используя конструктор копирования и затем не выбрасывающий исключения std::swap , мы можем быть уверены, что не может возникнуть промежуточного состояния с непроинициализированной памятью, если где-то произойдёт исключение). Все они используют функцию логирования, чтобы мы могли понять, когда они на самом деле вызваны.

Давайте запустим простой код, который копирует содержимое v1 в v2 :

И вот, что мы увидим:

Что совершенно логично, так как это точно отражает, что происходит внутри оператора присваивания. Но давайте предположим, что мы хотим присвоить v2 некоторое rvalue:

Хотя здесь я только присваиваю значение свеже созданному вектору, это является одной из демонстраций общего случая, когда некоторое временное rvalue создаётся и присваивается v2 (это может случится например, если функция возвращает вектор). Вот что мы увидим на экране:

Ого! Выглядит очень хлопотно. В частности, здесь потребовалась дополнительная пара вызовов конструктора с деструктором, чтобы создать, а потом удалить временный объект. И это печально, так как внутри копирующего оператора присваивания другой временный объект создаётся и удаляется. Дополнительная работа за зря.

Но, нет! C++11 даёт нам ссылки на rvalue, с помощью которых можно реализовать «семантику переноса», а в частности «переносящий оператор присваивания» (теперь понятно почему я всё время называл operator= копирующим оператором присваивания. В C++11 эта разница становится важной). Давайте добавим другой operator= в IntVec :

Двойной асперсанд — это ссылка на rvalue. Он означает как раз то, что и обещает — даёт ссылку на rvalue, который будет уничтожен после вызова. Мы можем использовать этот факт, чтобы просто «стащить» внутренности rvalue — они ему всё равно не нужны! Вот, что выведется на экран:

Как мы видим, вызывается новый переносящий оператор присваивания, так как rvalue присваивается v2 . Вызовы конструктора и деструктора всё же необходимы для временного объекта, который создаётся через Intvec(33) . Однако другой временный объект внутри оператора присваивания больше не нужен. Оператор просто меняет внутренний буфер rvalue со своим, и таким образом деструктор rvalue удаляет буфер самого объекта, который больше не будет использоваться. Чисто!

Хочу только отметить ещё раз, что этот пример только вершина айсберга семантики переноса и ссылок на rvalue. Как вы можете догадаться, это сложная тема с множеством частных случаев и загадок. Я пытался лишь продемонстрировать очень интересное применение различий между lvalue и rvalue в C++. Компилятор очевидно может их различать и позаботится о вызове правильного конструктора во время компиляции.

Заключение

Можно написать много C++ кода, не задумываясь о разногласиях rvalue и lvalue, опуская их как непонятный жаргон компилятора в сообщениях об ошибках. Однако, как я пытался показать в этой статье, лучшее владение этой темы обеспечит более глубокое понимание определённых конструкций C++, и сделает части стандарта C++ и дискуссии между экспертами языка для вас более доступными.

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

Источник

Понравилась статья? Поделить с друзьями:
  • Error lua gui forms playerseditorform helpers lua 300 attempt to index a nil value field
  • Error lp011 section placement failed iar
  • Error low level components
  • Error looser throw specifier for virtual
  • Error lookup скачать торрент