В С++ 03 выражение имеет значение rvalue или lvalue.
В С++ 11 выражение может быть:
Две категории стали пятью категориями.
Я предполагаю, что этот документ может стать не столь коротким введением: n3055
Вся резня началась с семантики движения. Как только у нас есть выражения, которые можно перемещать и не копировать, внезапно легко понять правила, требующие различия между выражениями, которые можно перемещать, и в каком направлении.
Из того, что я думаю, основываясь на проекте, различие значений r/l остается неизменным, только в контексте перемещения вещей становится беспорядочным.
Они нужны? Наверное, нет, если мы хотим потерять новые возможности. Но чтобы обеспечить лучшую оптимизацию, мы должны, вероятно, охватить их.Цитата n3055:
E
является
выражение типа указателя, затем *E
является выражением lvalue, относящимся к
объект или функцию, к которой E
точки. В качестве другого примера
результат вызова функции, чья
Тип возврата - это значение lvalue
lvalue.]Документ, о котором идет речь, является отличной ссылкой на этот вопрос, поскольку он показывает точные изменения в стандарте, которые произошли в результате введения новой номенклатуры.
Что представляют собой эти новые категории выражений?
FCD (n3092) имеет отличное описание:
- lvalue (так называемый, исторически, поскольку lvalues могут появляться на левая часть задания выражение) обозначает функцию или объект. [Пример: если E является выражение типа указателя, тогда * E - выражение lvalue, относящееся к объекту или функции, к которым E точки. В качестве другого примера, результат вызова функции, возвращение которой type является ссылкой на lvalue именующий. -end пример]
- значение x (an Значение "eXpiring" также относится к объект, обычно ближе к концу (так что его ресурсы могут быть перемещен, например). Значение x является результат определенных видов выражений с использованием ссылок rvalue (8.3.2). [ Пример: результат вызова функция, возвращающий тип которой Ссылка rvalue - это значение x. -конец пример]
- Значение glvalue ( "обобщенное" lvalue) является lvalue или xvalue.
- Rvalue (так называемый, исторически, потому что rvalues могут появиться на правая часть задания выражения) является x значением, временным объект (12.2) или его подобъект или значение, которое не связано с объект.
- PRvalue ( "чистое" значение r) значение r, которое не является значением x. [ Пример: результат вызова функция, тип возврата которой не является ссылка - это prvalue. Значение a буквальный, такой как 12, 7.3e5, или true также prvalue. -end пример]
Каждый выражение принадлежит точно одному из фундаментальные классификации в эта таксономия: lvalue, xvalue или prvalue. Это свойство выражение называется его значением категория. [Примечание: обсуждение каждый встроенный оператор в разделе 5 указывает категорию ее ценности доходности и категории значений операндов, которых он ожидает. Например, встроенные операторы присваивания ожидают что левый операнд является lvalue и что правый операнд является prvalue и в результате получим lvalue. Определяемые пользователем операторы - это функции, и категории ценностей, которые они ожидание и доходность определяются их параметров и типов возврата. -конец примечание
Я предлагаю вам прочитать весь раздел 3.10 Lvalues и rvalues , хотя.
Как эти новые категории относятся к существующим категориям rvalue и lvalue?
Снова:
Являются ли категории rvalue и lvalue в С++ 0x такими же, как и в С++ 03?
Семантика rvalues развилась особенно с введением семантики перемещения.
Почему нужны эти новые категории?
Таким образом, конструкция/назначение перемещения можно определить и поддерживать.
Я начну с вашего последнего вопроса:
Почему нужны эти новые категории?
Стандарт С++ содержит множество правил, относящихся к категории значений выражения. В некоторых правилах проводится различие между значениями lvalue и rvalue. Например, когда дело доходит до разрешения перегрузки. Другие правила делают различие между glvalue и prvalue. Например, вы можете иметь glvalue с неполным или абстрактным типом, но не существует prvalue с неполным или абстрактным типом. Прежде, чем мы получили эту терминологию, правила, которые действительно должны различать glvalue/prvalue, относящиеся к lvalue/rvalue, и они либо были непреднамеренно неправильными, либо содержали множество объяснений и исключений из правила a la "... если значение r должно быть вызвано неназванным rvalue reference...". Таким образом, кажется хорошей идеей просто дать понятия glvalues и prvalues собственное имя.
Что это за новые категории выражений? Как эти новые категории относятся к существующим категориям rvalue и lvalue?
Мы все еще имеем слова lvalue и rvalue, которые совместимы с С++ 98. Мы просто разделили rvalues на две подгруппы, xvalues и prvalues, и мы будем называть lvalues и xvalues как glvalues. Xvalues являются новой категорией значений для неназванных ссылок rvalue. Каждое выражение является одним из трех: lvalue, xvalue, prvalue. Диаграмма Венна будет выглядеть так:
______ ______
/ X \
/ / \ \
| l | x | pr |
\ \ / /
\______X______/
gl r
Примеры с функциями:
int prvalue();
int& lvalue();
int&& xvalue();
Но также не забывайте, что названные ссылки rvalue являются lvalues:
void foo(int&& t) {
// t is initialized with an rvalue expression
// but is actually an lvalue expression itself
}
Почему нужны эти новые категории? Являются ли боги WG21 просто путают нас простых смертных?
Я не чувствую, что другие ответы (хорошо, хотя многие из них) действительно фиксируют ответ на этот конкретный вопрос. Да, эти категории и такие существуют, чтобы позволить семантику перемещения, но сложность существует по одной причине. Это одно нерушимое правило перемещения вещей в С++ 11:
Ты должен двигаться только тогда, когда это, безусловно, это безопасно.
Вот почему существуют эти категории: иметь возможность говорить о ценностях, где можно безопасно переходить от них, и говорить о том, где это не так.
В ранней версии ссылок r-value движение происходило легко. Слишком легко. Достаточно легко, что существует много возможностей для неявного перемещения вещей, когда пользователь на самом деле не имел в виду.
Вот обстоятельства, при которых безопасно что-то перемещать:
Если вы это сделаете:
SomeType &&Func() { ... }
SomeType &&val = Func();
SomeType otherVal{val};
Что это делает? В более старых версиях спецификации до появления 5 значений это спровоцировало бы движение. Конечно. Вы передали ссылку rvalue на конструктор и, таким образом, привязываетесь к конструктору, который принимает ссылку rvalue. Это очевидно.
Там только одна проблема с этим; вы не просили его переместить. О, вы могли бы сказать, что &&
должно было быть ключом, но это не меняет того факта, что он нарушил правило. val
не является временным, потому что временные имена не имеют имен. Возможно, вы продлевали срок службы временного, но это означает, что он не является временным; это как и любая другая переменная стека.
Если это не временное, и вы не просили его переместить, то перемещение происходит неправильно.
Очевидное решение состоит в том, чтобы сделать val
значением lvalue. Это означает, что вы не можете перейти от него. Хорошо; он назвал, поэтому его значение.
Как только вы это сделаете, вы уже не можете сказать, что SomeType&&
означает одно и то же. Теперь вы сделали различие между названными ссылками rvalue и неназванными ссылками rvalue. Ну, названные ссылки rvalue - lvalues; это было наше решение выше. Итак, что мы называем неназванными ссылками rvalue (возвращаемое значение из Func
выше)?
Это не lvalue, потому что вы не можете перейти от lvalue. И мы должны иметь возможность двигаться, возвращая &&
; как еще вы могли бы прямо сказать что-то переместить? Это то, что std::move
возвращает, в конце концов. Это не rvalue (old-style), потому что это может быть в левой части уравнения (вещи на самом деле немного сложнее, см. Этот вопрос и комментарии ниже). Это ни lvalue, ни rvalue; это новый вид вещей.
У нас есть ценность, которую вы можете рассматривать как lvalue, за исключением того, что она неявно перемещается. Мы называем это значением x.
Обратите внимание, что xvalues - это то, что заставляет нас получать две другие категории значений:
Prvalue на самом деле просто новое имя для предыдущего типа rvalue, то есть они являются значениями, которые не являются значениями xvalues.
Glvalues - это объединение значений x и lvalues в одной группе, потому что они действительно имеют много общих свойств.
Так что действительно, все сводится к значениям x и необходимости ограничивать движение точно и только в определенных местах. Эти места определяются категорией rvalue; prvalues - это неявные движения, а xvalues - явные перемещения (std::move
возвращает значение xvalue).
Func
у Func
не должно быть оператора return?
&&
.
IMHO, лучшее объяснение его значения дало нам Stroustrup + принять во внимание примеры Dániel Sándor и Мохан:
Страуструп:
Теперь я серьезно волновался. Ясно, что мы двинулись в тупик или беспорядок или и то, и другое. Я провел обеденное время, проведя анализ, чтобы посмотреть, какие свойств (значений) были независимыми. Было всего два независимые свойства:
has identity
- то есть и адрес, указатель, пользователь может определить, идентичны ли две копии и т.д.can be moved from
- то есть нам разрешено оставить источник "копии" в некотором неопределенном, но действительном состоянииЭто привело меня к выводу, что существует ровно три вида значения (с использованием новационного трюка регулярного выражения с использованием заглавной буквы указывают на негатив - я спешил):
iM
: имеет личность и не может быть перемещен изiM
: имеет личность и может быть перемещен из (например, результат литья lvalue в ссылку rvalue)iM
: не имеет идентификатора и может быть перенесен с четвертой возможности (iM
: не имеет идентификатора и не может быть перемещен) не является полезно вC++
(или, я думаю) на любом другом языке.В дополнение к этим трем фундаментальным классификациям значений мы имеют два очевидных обобщения, которые соответствуют двум независимые свойства:
i
: имеет личностьm
: можно перемещать изЭто заставило меня поставить эту диаграмму на доске:
Нейминг
Я заметил, что у нас была только ограниченная свобода: два момента левые (обозначенные
iM
иi
) - это люди с более или менее формальность назвалиlvalues
, а две точки справа (обозначенныеm
иiM
) - это люди с большей или меньшей формальностью назвалиrvalues
. Это должно отразиться на нашем наименовании. То есть, левая "нога"W
должна иметь имена, связанные сlvalue
, и правая "нога"W
должна иметь имена, связанные сrvalue.
. Я отмечаю что вся эта дискуссия/проблема возникает из-за введения rvalue ссылки и перемещение семантики. Эти понятия просто не существуют в мире Стрэши, состоящем всего изrvalues
иlvalues
. Кто то заметил, что идеи, что
- Каждый
value
является либоlvalue
, либоrvalue
- An
lvalue
не являетсяrvalue
, аrvalue
не являетсяlvalue
глубоко погружены в наше сознание, очень полезные свойства и следы этой дихотомии можно найти по всему проекту стандарта. Мы все согласились, что мы должны сохранить эти свойства (и сделать их точный). Это еще больше ограничило наши варианты именования. Я заметил, что стандартная редакция библиотеки использует
rvalue
для обозначенияm
( обобщение), чтобы сохранить ожидания и текст стандартная библиотека, правая нижняя точкаW
должна быть названаrvalue.
Это привело к целенаправленному обсуждению именования. Во-первых, нам нужно было решить on
lvalue.
Еслиlvalue
означаетiM
или обобщениеi
? Светодиод Дуг Грегор, мы перечислили места в формулировке основного языка где словоlvalue
было квалифицировано как означающее тот или иной. список был сделан и в большинстве случаев и в самом сложном/хрупком текстеlvalue
в настоящее время означаетiM
. Это классический смысл lvalue потому что "в старые времена" ничего не тронуло;move
- новое понятие вC++0x
. Кроме того, называние точки верхнего пределаW
lvalue
дает нам свойство, что каждое значение являетсяlvalue
илиrvalue
, но не тем и другим.Итак, верхняя левая точка
W
равнаlvalue
, а нижняя правая точка isrvalue.
Что это делает нижние левые и верхние правые точки? Нижняя левая точка представляет собой обобщение классического значения lvalue, позволяя двигаться. Итак, этоgeneralized lvalue.
Мы назвали егоglvalue.
Вы можете спорить об аббревиатуре, но (я думаю) не с логикой. Мы предположили, что при серьезном использованииgeneralized lvalue
как-нибудь сократится, так что нам лучше это сделать немедленно (или смутить риск). Верхняя правая точка W меньше общий, чем нижний правый (теперь, как всегда, называетсяrvalue
). Что точка представляет собой оригинальное чистое понятие объекта, который вы можете перемещать из-за того, что его нельзя ссылаться снова (кроме деструктора). Мне понравилась фразаspecialized rvalue
в отличие отgeneralized lvalue
, ноpure rvalue
сокращено доprvalue
выиграла (и вероятно, правильно). Итак, левая нога W равнаlvalue
иglvalue
и правая ногаprvalue
иrvalue.
Кстати, каждое значение - либо значение glvalue, либо prvalue, но не оба.Это оставляет верхнюю середину
W
:iM
; то есть значения, которые имеют идентичность и может быть перемещена. У нас действительно нет ничего, что поможет нас к доброму имени для этих эзотерических зверей. Они важны для люди, работающие со стандартным текстом (проект), но вряд ли стать фамилией. Мы не нашли реальных ограничений на называя нас руководством, поэтому мы выбрали 'x для центра, неизвестного, странный, только xpert или даже x-rated.
ISOС++ 11 (официально ISO/IEC 14882: 2011) является самой последней версией стандарта языка программирования С++. Он содержит некоторые новые функции и концепции, например:
Если мы хотим понять концепции новых категорий значений выражения, мы должны знать, что существуют ссылки rvalue и lvalue. Лучше знать, что rvalues могут быть переданы в неконстантные ссылки rvalue.
int& r_i=7; // compile error
int&& rr_i=7; // OK
Мы можем получить некоторую интуицию понятий категорий ценностей, если мы приводим подраздел под заголовками Lvalues и rvalues из рабочего проекта N3337 (самый похожий проект для опубликованного стандарта ISOС++ 11).
3.10 Lvalues и rvalues [basic.lval]
1 Выражения классифицируются в соответствии с таксономией на рисунке 1.
- Значение l (так называемое, исторически, поскольку lvalues может появиться в левой части выражения присваивания) обозначает функцию или объект. [Пример: если E является выражением типа указателя, тогда * E - выражение lvalue, относящееся к объекту или функции, к которой указывает E. В качестве другого примера, результат вызова функции чей тип возврата является ссылкой lvalue, является значением lvalue. -end пример]
- Значение xvalue (значение "eXpiring" ) также относится к объекту, обычно ближе к концу его жизненного цикла (чтобы его ресурсы могли перемещаться, для пример). Значение x является результатом определенных видов выражений с использованием ссылок rvalue (8.3.2). [Пример: результат вызова функция, возвращающим тип которой является ссылкой rvalue, является значением x. -конец пример]
- Значение glval ( "обобщенное" lvalue) является значением lvalue или значением x.
- Значение rvalue (так называемое, исторически, поскольку rvalues может отображаться в правой части выражения присваивания) является xvalue, a временный объект (12.2) или его подобъект или значение, которое не является связанные с объектом.
- Значение prvalue ( "чистое" значение r) является значением r, которое не является значением x. [Пример: результат вызова функции, возвращаемый тип которой не является ссылка - это prvalue. Значение литерала, такого как 12, 7.3e5, или
true также является prvalue. -end пример]Каждое выражение принадлежит точно одному из фундаментальных классификации в этой таксономии: lvalue, xvalue или prvalue. Эта свойство выражения называется его категорией значений.
Но я не совсем уверен в том, что этого подраздела достаточно, чтобы понять понятия ясно, потому что "обычно" на самом деле не является общим, "ближе к концу его жизни" на самом деле не является конкретным, "с использованием ссылок rvalue" не является действительно ясно, и "Пример: результат вызова функции, тип возврата которой является ссылкой rvalue, является значением xvalue". звучит как змея, кусающая хвост.
Каждое выражение относится к одной первичной категории значений. Эти категории значений - это значения lvalue, xvalue и prvalue.
Выражение E относится к категории lvalue тогда и только тогда, когда E относится к сущности, которая УЖЕ имела идентификатор (адрес, имя или псевдоним), который делает его доступным вне E.
#include <iostream>
int i=7;
const int& f(){
return i;
}
int main()
{
std::cout<<&"www"<<std::endl; // This address ...
std::cout<<&"www"<<std::endl; // ... and this address are the same.
"www"; // The expression "www" in this row is an lvalue expression, because it refers to the same entity ...
"www"; // ... as the entity the expression "www" in this row refers to.
i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
i; // ... as the entity the expression i in this row refers to.
int* p_i=new int(7);
*p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
*p_i; // ... as the entity the expression *p_i in this row refers to.
const int& r_I=7;
r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
r_I; // ... as the entity the expression r_I in this row refers to.
f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
i; // ... as the entity the expression f() in this row refers to.
return 0;
}
Выражение E относится к категории xvalue тогда и только тогда, когда оно
- результат вызова функции, неявно или явно, чей тип возврата является ссылкой rvalue на тип возвращаемого объекта или
int&& f(){
return 3;
}
int main()
{
f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.
return 0;
}
- ссылка на ссылку rvalue для типа объекта или
int main()
{
static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).
return 0;
}
- выражение доступа к члену класса, обозначающее нестатический элемент данных не ссылочного типа, в котором выражение объекта является значением x, или
struct As
{
int i;
};
As&& f(){
return As();
}
int main()
{
f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.
return 0;
}
- выражение указателя на элемент, в котором первый операнд является значением x, а второй операнд является указателем на элемент данных.
Обратите внимание, что эффект приведенных выше правил заключается в том, что именованные ссылки rvalue для объектов рассматриваются как lvalues, а неназванные ссылки rvalue на объекты рассматриваются как xvalues; Ссылки rvalue на функции обрабатываются как lvalues, именованные или нет.
#include <functional>
struct As
{
int i;
};
As&& f(){
return As();
}
int main()
{
f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
As&& rr_a=As();
rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.
return 0;
}
Выражение E принадлежит к категории prvalue тогда и только тогда, когда E не принадлежит ни к значению, ни к категории значений x.
struct As
{
void f(){
this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
}
};
As f(){
return As();
}
int main()
{
f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.
return 0;
}
Существуют две дополнительные важные категории смешанных значений. Эти категории значений представляют собой категории rvalue и glvalue.
Выражение E относится к категории rvalue тогда и только тогда, когда E принадлежит категории xvalue или к категории prvalue.
Обратите внимание, что это определение означает, что выражение E относится к категории rvalue тогда и только тогда, когда E относится к объекту, у которого не было никакого идентификатора, что делает его доступным вне E YET.
Выражение E принадлежит к категории glvalue тогда и только тогда, когда E принадлежит категории lvalue или к категории значений x.
Скотт Мейер опубликовал очень полезное эмпирическое правило, чтобы отличать rvalues от lvalues.
- Если вы можете взять адрес выражения, выражение будет lvalue.
- Если тип выражения является ссылкой на lvalue (например, T & или const T & и т.д.), это выражение является lvalue.
- В противном случае выражение является rvalue. Концептуально (и, как правило, также и на самом деле), rvalues соответствуют временным объектам, таким как те, которые возвращаются из функций или создаются с помощью неявного типа преобразования. Большинство литеральных значений (например, 10 и 5.3) также являются значениями.
С++ 03 слишком ограничены, чтобы правильно вносить ссылки rvalue в атрибуты выражений.
С их введением было сказано, что ссылка на неназванный rvalue оценивается как rvalue, так что разрешение перегрузки будет предпочтительнее привязки ссылок rvalue, что сделает его выборными конструкторами перемещения над конструкторами копирования. Но было обнаружено, что это вызывает проблемы вокруг, например, с Динамические типы и с квалификацией.
Чтобы показать это, рассмотрим
int const&& f();
int main() {
int &&i = f(); // disgusting!
}
В предварительных черновиках это было разрешено, потому что в С++ 03 значения типов неклассов никогда не имеют квалификации. Но предполагается, что const
применяется в случае с rvalue-reference, потому что здесь мы ссылаемся на объекты (= память!), А drop const из неклассических значений r главным образом по той причине, что вокруг объекта нет объекта.
Проблема для динамических типов имеет сходный характер. В С++ 03 значения типа класса имеют известный динамический тип - это статический тип этого выражения. Потому что, чтобы иметь это по-другому, вам нужны ссылки или различия, которые оцениваются до значения lvalue. Это неверно с неназванными ссылками rvalue, но они могут демонстрировать полиморфное поведение. Чтобы решить эту проблему,
ссылки без имени rvalue становятся xvalues . Они могут быть квалифицированными и потенциально отличаться от их динамического типа. Они, как и предполагалось, предпочитают ссылки rvalue во время перегрузки и не свяжутся с неконстантными ссылками lvalue.
То, что раньше было rvalue (литералы, объекты, созданные приведениями к типам без ссылки), теперь становится prvalue. Они имеют то же предпочтение, что и значения x во время перегрузки.
То, что ранее было lvalue, остается lvalue.
И две группы выполняются для захвата тех, которые могут быть квалифицированы и могут иметь разные динамические типы (glvalues ) и те, где перегрузка предпочитает привязку ссылки rvalue ( rvalues ).
Я долгое время боролся с этим, пока не наткнулся на объяснение cppreference.com категорий .
Это на самом деле довольно просто, но я нахожу, что его часто объясняют так, что трудно запомнить. Здесь это объясняется очень схематично. Я приведу некоторые части страницы:
Основные категории
Категории первичных значений соответствуют двум свойствам выражений:
имеет личность: можно определить, относится ли выражение к тому же объекту, что и другое выражение, например, путем сравнения адресов объектов или функций, которые они идентифицируют (получаются прямо или косвенно);
можно перенести из: move constructor, move assign operator или другую перегрузку функции, которая реализует семантику перемещения, может привязываться к выражению.
Выражения, которые:
- имеют идентификатор и не могут быть перенесены из них, называются выражениями lvalue;
- имеют личность и могут быть перемещены из называются выражениями xvalue;
- не имеют идентификатора и могут быть перемещены из вызываемых выражений prvalue;
- не имеют идентификатора и не могут быть перемещены из него.
именующего
выражение lvalue ( "left value" ) является выражением, которое имеет идентичность и не может быть перемещено.
rvalue (до С++ 11), prvalue (начиная с С++ 11)
выражение prvalue ( "pure rvalue" ) - это выражение, которое не имеет идентификатора и может быть перенесено из.
xvalue
Выражение xvalue ( "expired value" ) является выражением, которое имеет идентификатор и может быть перемещено из.
glvalue
выражение glvalue ( "generalized lvalue" ) является выражением, которое является либо значением lvalue, либо значением x. Он имеет идентичность. Он может быть или не быть перемещен из.
rvalue (поскольку С++ 11)
выражение rvalue ( "right value" ) является выражением, которое является либо значением prvalue, либо значением x. Его можно перенести. Он может иметь или не иметь идентичности.
Как эти новые категории относятся к существующим категориям rvalue и lvalue?
A С++ 03 lvalue по-прежнему является значением С++ 11 lvalue, тогда как значение С++ 03 rvalue называется prvalue в С++ 11.
Одно дополнение к превосходным ответам выше, на том, что меня смутило даже после того, как я прочитал "Страуструп" и подумал, что я понимаю разницу в значении/значении. Когда вы видите
int&& a = 3
,
очень заманчиво читать int&&
как тип и заключить, что a
является значением r. Это не так:
int&& a = 3;
int&& c = a; //error: cannot bind 'int' lvalue to 'int&&'
int& b = a; //compiles
a
имеет имя и ipso facto lvalue. Не думайте о &&
как о части a
; это просто что-то говорит вам, к чему a
разрешено связывать.
Это особенно важно для аргументов типа T&&
в конструкторах. Если вы пишете
Foo::Foo(T&& _t) : t{_t} {}
вы скопируете _t
в t
. Вам нужно
Foo::Foo(T&& _t) : t{std::move(_t)} {}
, если вы хотите переместить. Если бы мой компилятор предупредил меня, когда я оставил move
!
a
»: Конечно, но в строках 2 и 3 ваши переменные - это c & b, и это не то, с чем связывается, и тип a
здесь не имеет значения, не так ли? Строки будут одинаковыми, если a
объявлено как int a
. Фактическая основная разница состоит в том , что в строке 1 а , не должны быть const
связываться с 3.
В С++ переменные являются типом l-значения (произносится как ell-value). Значение l - это значение, которое имеет постоянный адрес (в памяти). Поскольку все переменные имеют адреса, все переменные являются значениями l. Имя l-значение возникло из-за того, что l-значения являются единственными значениями, которые могут быть в левой части оператора присваивания. Когда мы выполняем задание, левая часть оператора присваивания должна быть l-значением. Следовательно, утверждение типа 5 = 6; приведет к ошибке компиляции, поскольку 5 не является значением l. Значение 5 не имеет памяти, и, следовательно, ничто не может быть назначено ему. 5 означает 5, и его значение не может быть переназначено. Когда l-value имеет назначенное ему значение, текущее значение на этом адресе памяти перезаписывается.
Противоположностью l-значениям являются r-значения (выраженные arr-значения). Значение r относится к значениям, которые не связаны с постоянным адресом памяти. Примерами r-значений являются одиночные числа (например, 5, которые оцениваются до 5) и выражения (например, 2 + x, который оценивает значение переменной x плюс 2). r-значения обычно носят временный характер и отбрасываются в конце заявления, в котором они встречаются.
Ниже приведен пример некоторых операторов присваивания, показывающих, как оцениваются значения r:
int y; // define y as an integer variable
y = 4; // r-value 4 evaluates to 4, which is then assigned to l-value y
y = 2 + 5; // r-value 2 + r-value 5 evaluates to r-value 7, which is then assigned to l-value y
int x; // define x as an integer variable
x = y; // l-value y evaluates to 7 (from before), which is then assigned to l-value x.
x = x; // l-value x evaluates to 7, which is then assigned to l-value x (useless!)
x = x + 1; // l-value x + r-value 1 evaluate to r-value 8, which is then assigned to l-value x.
Давайте рассмотрим последний оператор присваивания выше, поскольку он вызывает наибольшую путаницу.
x = x+1
В этом утверждении переменная x используется в двух разных контекстах. В левой части оператора присваивания "x" используется как l-значение (переменная с адресом), в котором сохраняется значение. С правой стороны оператора присваивания x вычисляется для получения значения (в данном случае 7). Когда С++ оценивает вышеуказанный оператор, он оценивается как:
x = 7+1
Что делает очевидным, что С++ присваивает значение 8 обратно переменной x.
В настоящее время вам не нужно много беспокоиться о значениях l или значениях r, но лучше вернуться к ним позже, когда мы начнем обсуждение более продвинутых тем.
Ключевым выводом здесь является то, что в левой части задания вы должны иметь то, что представляет собой адрес памяти (например, переменную). Все, что находится в правой части задания, будет оценено для получения значения.
x
гдеx
- этоint
может использоваться как l-значение или как r-значение.