Введение в Rvalue и Lvalue
С появлением C ++ 11 появилась возможность еще больше повысить производительность и гибкость приложений C ++ благодаря введению семантики перемещения и идеальной пересылки. Две концепции, основанные на ссылках на rvalue.
Прежде чем обсуждать эти новые концепции, необходимо понять rvalues и lvalues. Эти концепции сами по себе не слишком сложны, но развивающаяся природа C ++ и изменения в определении этих значений по мере развития языка могут сделать их довольно запутанными для новых разработчиков.
Вначале rvalue и lvalue были определены таким образом, что rvalue очень просто означало что-то, что появляется в правой части присваивания, а lvalue - слева. Например:
int num = 3; 4 = num; // 4 is a literal which is an rvalue and num is an lvalue thus the following code isn’t valid
Но допустимо что-то вроде следующего:
int num = 4; // num is an lvalue and 4 is an rvalue
Время шло, и язык C ++ развивался, вместе с определениями rvalues и lvalues. К сожалению, их имена не эволюционировали вместе с этим. В более современных версиях C ++ rvalues и lvalues приобрели новое значение.
Lvalues и rvalues стали чем-то, что использовалось для описания результатов выражения (выражение - это любой код, возвращающий значение), и как такое выражение можно было бы описать либо как выражение lvalue, либо как выражение rvalue.
Для компилятора это, помимо прочего, определяет, как сохраняется результат выражения. Для разработчика, как я предполагаю, это в основном означает, что lvalue имеет имя и идентифицируемое место в памяти, к которому можно получить доступ с помощью ‘&’. Rvalue - это то, что не имеет идентифицируемого местоположения в памяти, и поэтому к нему нельзя получить доступ с помощью «&».
Простой способ определить, возвращает ли выражение rvalue или lvalue, - это определить, можете ли вы взять адрес значения. Если вы можете это lvalue, иначе это rvalue.
Значения R являются временными и имеют короткое время жизни, если они существуют в выражении, которое они были созданы. Из-за этого C ++ не разрешает доступ к ним через адрес оператора из-за большого количества проблем, которые могут возникнуть при попытке доступа к адресу короткоживущих данных.
На этом все заканчивается тем, что такое rvalues и lvalues, но я могу предположить, что приведенное выше определение мало что дает для объяснения их фактического использования при написании приложений на C ++. Я попытаюсь исправить это в следующем разделе, объяснив, как они могут вызывать проблемы при написании кода.
Как упоминалось ранее, вы не можете получить адрес rvalue, потому что C ++ не позволяет этого. Это означает, что при написании кода существует множество значений, на которые мы не можем ссылаться. Литералы, функции, возвращающие по значению, и временные объекты - лишь несколько примеров. Это означает, что ссылка на эти значения не может быть сохранена, но, что более важно, эти значения не могут быть переданы функциям, которые принимают ссылочные типы.
К сожалению, передача по ссылке - это распространенный метод повышения производительности приложения, поскольку он позволяет избежать необходимости копировать данные между ячейками памяти, что при большом количестве данных может занять слишком много времени в наших критичных по времени приложениях.
Поэтому, чтобы избежать этих проблем, мы можем предпринять несколько шагов. Просто попытайтесь избежать передачи rvalue функциям, что означает следующий код:
int sum(int& a, int& b) { return a + b; Sum(3, 5);
Надо было бы написать так:
int sum(int& a, int& b) { return a + b; int a = 5; int b = 3; int c = sum(a, b);
Удобство использования rvalue теряется для поддержания производительности, но за счет удобочитаемости и времени. Второй вариант - передать по значению, но это потенциально может снизить производительность. Существует третий вариант - передача по ссылке, но с сохранением параметра функции константой.
int sum(const int& a, const int& b) { return a + b; Sum(3, 5);
C ++ позволяет присвоить rvalue ссылке, которая является lvalue, тогда и только тогда, когда это lvalue определено как const, что предотвращает манипулирование им. Это означает, что rvalue технически является lvalue в рамках функции, и теперь функция может принимать rvalue, сохраняя при этом передачу по ссылке.
Возникает очевидная проблема: если функции необходимо манипулировать ссылкой, она не может без перегрузки этой функции неконстантным вариантом, когда требуется передать lvalue. Rvalues вызывает дополнительные проблемы в C ++, которые не решались до C ++ 11.
Например, C ++ имеет тенденцию чрезмерно баловаться, когда дело доходит до копирования значений, и есть проблемы с пересылкой значений функциям, вызываемым в шаблонных функциях. С этими проблемами борются с помощью семантики перемещения и идеальной пересылки, которую будет намного легче понять теперь, когда у вас есть понимание rvalue и lvalues, и она будет в центре внимания следующих нескольких статей.
Как всегда, если у вас есть вопросы, напишите мне @gamedevunboxed :)