ошибка: недопустимая инициализация неконстантной ссылки типа 'int &' из rvalue типа 'int'

87

Неправильная форма:

int &z = 12;

Правильная форма:

int y;
int &r = y;

Вопрос :
Почему первый код неправильный? В чем " смысл " ошибки в названии?

Водолей - девушка
источник
4
Временные объекты не могут быть привязаны к непостоянным ссылкам. int (12) в этом случае является временным.
Prasoon Saurav
@PrasoonSaurav Что вы имеете в виду под временными 12? Отсутствие концепций здесь (с моей стороны :))
Aquarius_Girl
2
Обратите внимание, что для этого ограничения нет строгой технической причины. Так же легко было бы реализовать возможность изменения ссылок на временные библиотеки. Запрещение этого - проектное решение C ++, так как такая конструкция была бы плохой конструкцией с гораздо большим риском непреднамеренного злоупотребления, чем подлинная полезность. (Я только однажды обнаружил надуманную потребность в такой вещи.)
Керрек С.Б.
@KerrekSB - самая распространенная потребность в привязке ref к объекту rvalue(ostringstream() << "x=" << x).str()
любопытный парень
@curiousguy: Да, это содержание опубликованной мной ссылки.
Kerrek SB

Ответы:

132

В C ++ 03 3.10 / 1 сказано: «Каждое выражение является либо lvalue, либо rvalue». Важно помнить, что ценность по сравнению с ценностью - это свойство выражений, а не объектов.

Lvalues ​​именуют объекты, которые сохраняются за пределами одного выражения. Так , например, obj, *ptr, ptr[index]и ++xвсе lvalues.

Rvalue - это временные значения, которые испаряются в конце полного выражения, в котором они живут («точка с запятой»). Так , например, 1729, x + y, std::string("meow")и x++все rvalues.

Оператор адреса требует, чтобы его «операнд был lvalue». если бы мы могли взять адрес одного выражения, это выражение было бы lvalue, иначе это rvalue.

 &obj; //  valid
 &12;  //invalid
БрюсАди
источник
2
« если бы мы могли взять адрес одного выражения, выражение было бы lvalue, иначе - rvalue». Если бы только C ++ был таким простым! (но нюанс здесь не очень актуален) "R-значения временные" какие временные? объекты?
curiousguy
2
@curiousguyRvalues ​​- это временные значения, которые исчезают в конце полного выражения, в котором они живут. Чтобы просто ответить, почему выражение int & = 12;недействительно, стандарт говорит, что строковый литерал является lvalue, другие литералы - rvalue.
BruceAdi
@curiousguy: Да. Rvalues ​​являются временными объектами , но не все rvalue являются временными объектами; некоторые даже не объекты.
Nawaz
« Например, 1729, x + y, std :: string (« мяу ») и x ++ - все rvalue. » Но std::string("meow")конструирует объект типа std::stringи выдает rvalue, которое обозначает этот объект, 1729не имеет побочных эффектов и дает значение 1729 как значение типа int.
curiousguy
1
@curiousguy: Это "Lvalues name objects that persist beyond a single expression."на 100% правильное утверждение. Напротив, ваш пример (const int &)1неверен, потому что это НЕ «именованный» объект.
Nawaz
53
int &z = 12;

С правой стороны временный объект типа intсоздается из интегрального литерала 12, но временный объект не может быть привязан к неконстантной ссылке. Отсюда и ошибка. Это то же самое, что:

int &z = int(12); //still same error

Почему создается временное? Поскольку ссылка должна ссылаться на объект в памяти, и для того, чтобы объект существовал, он должен быть сначала создан. Поскольку объект не имеет имени, это временный объект. У него нет названия. Из этого объяснения стало довольно ясно, почему второй случай хорош.

Временный объект может быть привязан к ссылке const, что означает, что вы можете сделать это:

const int &z = 12; //ok

Справочник по C ++ 11 и Rvalue:

Для полноты картины я хотел бы добавить, что в C ++ 11 введена ссылка на rvalue, которая может связываться с временным объектом. Итак, в C ++ 11 вы можете написать это:

int && z = 12; //C+11 only 

Обратите внимание, что есть &&intead of &. Также обратите внимание, что constэто больше не нужно, даже если объект, с которым zсвязывается, является временным объектом, созданным из интегрального литерала 12.

Поскольку в C ++ 11 введена ссылка на rvalue , int&отныне она называется lvalue-reference .

Наваз
источник
10

12- это константа времени компиляции, которую нельзя изменить в отличие от данных, на которые ссылается int&. Что вы можете сделать, это

const int& z = 12;
Михаил Крелин - хакер
источник
2
@curiousguy, будьте последовательны, вы сказали: «Вы должны понимать, что это правила C ++. Они существуют и не нуждаются в обосновании» (не говоря уже о первом издании). Как мне интерпретировать ваши жалобы на то, что вы не отдаете то, что, по вашему мнению, не нужно?
Михаил Крелин - хакер
2
@ MichaelKrelin-hacker: Технически нет, вы не можете (никогда) привязать ссылку к значению (или постоянной времени компиляции), стандарт довольно четко описывает, что на самом деле происходит: в противном случае создается временный объект типа «cv1 T1» и инициализируется из выражения инициализатора с использованием правил для инициализации не ссылочной копии (8.5). Затем ссылка привязывается к временному. То есть синтаксис разрешен, но семантика не связана с привязкой ссылки к константе, а скорее с привязкой ее к временному, которое неявно создается.
Дэвид Родригес - dribeas
1
@curiousguy: Правила языка являются частью дизайна, и чаще всего есть обоснования того, почему язык был разработан таким образом. В этом конкретном случае, как и в C, вам не разрешено брать адрес значения (у него его нет), и вы не можете привязать ссылку. Теперь рассмотрим функцию void f( vector<int> const & ), которая идиоматична для передачи вектора, который не должен быть изменен. Теперь проблема в том, что это f( vector<int>(5) )было бы неправильно, и пользователю пришлось бы предоставить другую перегрузку, void f( vector<int> v ) { f(v); }которая является тривиальной.
Дэвид Родригес - дрибэас
1
... теперь, поскольку это было бы болезненно для пользователей языка, дизайнеры решили, что компилятор выполнит эквивалентную операцию для вас, в вызове f( vector<int>(5) )компилятор создает временный, а затем связывает ссылку с этим временным, и аналогично если было неявное преобразование из 5напрямую. Это позволяет компилятору генерировать единую подпись для функции и позволяет реализовать функцию одним пользователем. С этого момента аналогичное поведение определяется для остальных случаев использования постоянных ссылок для согласованности.
Дэвид Родригес - dribeas
1
@ MichaelKrelin-hacker: ссылки - это псевдонимы объектов, а значение - не объект. В зависимости от контекста ссылки могут быть просто псевдонимом, компилятор удаляет ссылку и просто использует идентификатор для обозначения того, что имел в виду исходный объект ( T const & r = *ptr;любое последующее использование rв функции может быть заменено на *ptr, и rне обязательно должно существовать во время выполнения) или его, возможно, придется реализовать, сохранив адрес объекта, которому он присваивает псевдоним (рассмотрите возможность сохранения ссылки как члена объекта) - что реализовано как указатель с автоматическим разыменованием.
Дэвид Родригес - dribeas
4

Привязка неконстантных и константных ссылок подчиняется разным правилам

Это правила языка C ++:

  • выражение, состоящее из буквального числа ( 12), является "rvalue"
  • не разрешено создавать неконстантную ссылку с rvalue: неправильно int &ri = 12;сформировано
  • разрешено создавать константную ссылку с rvalue: в этом случае неименованный объект создается компилятором; этот объект будет существовать, пока существует сама ссылка.

Вы должны понимать, что это правила C ++. Они просто есть.

Легко изобрести другой язык, скажем C ++ ', с немного другими правилами. В C ++ 'было бы разрешено создать неконстантную ссылку с rvalue. Здесь нет ничего непоследовательного или невозможного.

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

любопытный парень
источник
0

Ссылки - это «скрытые указатели» (ненулевые) на вещи, которые могут изменяться (lvalues). Вы не можете определить их как константу. Это должна быть «переменная» вещь.

РЕДАКТИРОВАТЬ::

Я думаю о

int &x = y;

как почти эквивалент

int* __px = &y;
#define x (*__px)

где __px- новое имя, и #define xработает только внутри блока, содержащего объявление xссылки.

Василий Старынкевич
источник
1
Почему можно, если ссылка есть const:)
Михаил Крелин - хакер
Но на плакате примера не былоconst
Василий Старынкевич
Да, я имел в виду вашу формулировку «ссылки - это указатели на вещи, которые могут измениться» - речь идет о бесплатных ссылках.
Михаил Крелин - хакер
« Ссылки - это« скрытые указатели » « неправильные » вещи, которые могут изменить « неправильные » вещи, которые могут измениться (lvalues). « Неправильно »
любопытный парень
@Loki: не могли бы вы объяснить больше?
Василий Старынкевич