Почему неконстантная ссылка не может быть привязана к временному объекту?

Почему нельзя получить неконстантную ссылку на временный объект, который возвращает функция getx()? Ясно, что это запрещено стандартом C ++, но меня интересует цель такого ограничения, а не ссылка на стандарт.

struct X
{
    X& ref() { return *this; }
};

X getx() { return X();}

void g(X & x) {}    

int f()
{
    const X& x = getx(); // OK
    X& x = getx(); // error
    X& x = getx().ref(); // OK
    g(getx()); //error
    g(getx().ref()); //OK
    return 0;
}
  1. Понятно, что время жизни объекта не может быть причиной, потому что постоянная ссылка на объект не запрещена стандартом C ++.
  2. Ясно, что временный объект не является постоянным в приведенном выше примере, потому что разрешены вызовы непостоянных функций. Например, ref() может изменить временный объект.
  3. Вдобавок ref() позволяет обмануть компилятор и получить ссылку на этот временный объект, что решает нашу проблему.

Дополнительно:

Они говорят: «Назначение временного объекта константной ссылке продлевает время жизни этого объекта» и «Ничего не сказано о неконстантных ссылках». Мой дополнительный вопрос. Продлевает ли следующее задание срок жизни временного объекта?

X& x = getx().ref(); // OK

person Alexey Malistov    schedule 14.10.2009    source источник
comment
Я не согласен с тем, что время жизни объекта не может быть причиной, просто потому, что в стандарте указано, что присвоение временного объекта ссылке const увеличивает время жизни этого объекта до времени жизни ссылки const. Однако ничего не говорится о неконстантных ссылках ...   -  person SadSido    schedule 14.10.2009
comment
Что ж, в чем причина этого Ничего не сказано о неконстантных ссылках… Это часть моего вопроса. Есть ли в этом смысл? Может быть, авторы Standard просто забыли о неконстантных ссылках и скоро мы увидим следующий Core Issue?   -  person Alexey Malistov    schedule 14.10.2009
comment
Временные файлы будут привязываться к ссылкам rvalue, представленным в C ++ 0x. Я не думаю, что это было упущением в стандарте, поскольку ссылки rvalue открывают совершенно новую функциональность, что было бы невозможно, если бы ссылки lvalue были привязаны как к невременным, так и к временным файлам (в C ++ 0x у вас будет возможность сообщить их отдельно, и, следовательно, использовать семантику перемещения, а не безопасное копирование семантики).   -  person UncleBens    schedule 14.10.2009
comment
Кто-нибудь обсудит этот вопрос здесь: groups.google .com / группа / comp.lang.c ++ / browse_thread / thread /   -  person fnieto - Fernando Nieto    schedule 14.10.2009
comment
GotW # 88: кандидат на самую важную конструкцию. grassutter.spaces.live.com/blog/cns!2D4327CC297151BB!378. запись   -  person fnieto - Fernando Nieto    schedule 14.10.2009
comment
Очень хороший вопрос - одна интересная вещь, которую я обнаружил, заключается в том, что MSVC (даже до VS2008), похоже, принимает строки, которые другие компиляторы диагностируют как ошибки. Как это может быть?   -  person Michael Burr    schedule 14.10.2009
comment
@Michael: VC связывает rvalue с неконстантными ссылками. Они называют это функцией, но на самом деле это ошибка. (Обратите внимание, что это ошибка не потому, что она по своей сути нелогична, а потому, что она была исключена явно для предотвращения глупых ошибок.)   -  person sbi    schedule 14.10.2009
comment
@sbi - интересно ... есть ли указатель на какие-то документы по этому поводу? Я хотел бы прочитать, что М.С. мог бы сказать по этому поводу.   -  person Michael Burr    schedule 15.10.2009
comment
@Michael: Это где-то в MSDN. Придется искать самому. (Это может помочь поискать вопросы по этому поводу, поскольку на протяжении многих лет я видел, как это задают снова и снова. Я уверен, что кто-то где-то уже указывал на документы.)   -  person sbi    schedule 15.10.2009
comment
Я не вижу, где кто-нибудь ответил на последний вопрос OP.   -  person ThomasMcLeod    schedule 07.05.2014
comment
Начиная с Visual Studio 2015RC, он по-прежнему компилируется по умолчанию. С флагами /W4 или /Wall компилятор выдает предупреждение: C4239: nonstandard extension used: 'argument': conversion from 'type' to 'type&'. При отключении языковых расширений /Za он не работает с error C2664: cannot convert argument 1 from 'type' to 'type&'   -  person Ivan Aksamentov - Drop    schedule 11.06.2015


Ответы (11)


Из этого Статья в блоге Visual C ++ о ссылках на rvalue:

... C ++ не хочет, чтобы вы случайно изменили временные файлы, но прямой вызов неконстантной функции-члена для изменяемого rvalue является явным, поэтому он разрешен ...

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

На вашем месте я бы переосмыслил дизайн своих функций. Почему g () принимает ссылку, изменяет ли он параметр? Если нет, сделайте это константной ссылкой, если да, то почему вы пытаетесь передать ему временное значение, разве вас не волнует, что это временное значение, которое вы изменяете? Почему getx () все равно возвращается временно? Если вы поделитесь с нами своим реальным сценарием и тем, чего вы пытаетесь достичь, вы можете получить несколько хороших советов о том, как это сделать.

Противодействие языку и обман компилятора редко решают проблемы - обычно они создают проблемы.


Изменить: ответы на вопросы в комментариях: 1) X& x = getx().ref(); // OK when will x die? - Я не знаю и мне все равно, потому что это именно то, что я имею в виду, говоря «идти против языка». В языке говорится, что «временные объекты умирают в конце оператора, если они не привязаны к константной ссылке, и в этом случае они умирают, когда ссылка выходит за пределы области видимости». Применяя это правило, кажется, что x уже мертв в начале следующего оператора, поскольку он не привязан к ссылке const (компилятор не знает, что возвращает ref ()). Однако это всего лишь предположение.

2) Я четко сформулировал цель: вам не разрешено изменять временные файлы, потому что это просто не имеет смысла (игнорируя ссылки C ++ 0x rvalue). На вопрос «тогда почему мне разрешено вызывать неконстантные члены?» хороший, но у меня нет лучшего ответа, чем тот, который я уже сказал выше.

3) Что ж, если я прав насчет x in X& x = getx().ref(); dying в конце утверждения, проблемы очевидны.

В любом случае, судя по вашему вопросу и комментариям, я не думаю, что даже эти дополнительные ответы вас удовлетворит. Вот последняя попытка / резюме: комитет C ++ решил, что нет смысла изменять временные файлы, поэтому они запретили привязку к неконстантным ссылкам. Может быть, была задействована какая-то реализация компилятора или исторические проблемы, я не знаю. Затем возник какой-то конкретный случай, и было решено, что, несмотря ни на что, они все же разрешат прямую модификацию через вызов неконстантного метода. Но это исключение - вам обычно не разрешается изменять временные файлы. Да, C ++ часто бывает таким странным.

person sbk    schedule 14.10.2009
comment
1) являются временными объектами и умрут в любой момент. X& x = getx().ref(); // OK когда x умрет? 2) Я бы переосмыслил дизайн своих функций. Мой вопрос не о дизайне, а о цели такого ограничения. 3) обычно это создает проблемы. Какие проблемы создает в этом случае неконстантная ссылка на временный объект? - person Alexey Malistov; 14.10.2009
comment
@sbk: 1) На самом деле правильная фраза: ... в конце полного выражения .... Полное выражение, я полагаю, определяется как выражение, которое не является подвыражением какого-то другого выражения. Всегда ли это то же самое, что и конец заявления, я не уверен. - person sbi; 14.10.2009
comment
@sbk: 2) На самом деле вам разрешено изменять rvalue (временные). Это запрещено для встроенных типов (int и т. Д.), Но разрешено для пользовательских типов: (std::string("A")+"B").append("C"). - person sbi; 14.10.2009
comment
@sbk: 3) Причина, по которой Страуструп (в D&E) запрещает привязку rvalue к неконстантным ссылкам, заключается в том, что если g() Алексея изменит объект (чего можно ожидать от функции, принимающей неконстантную ссылку) , это изменит объект, который умирает, так что никто не сможет получить измененное значение в любом случае. Он говорит, что это, скорее всего, ошибка. - person sbi; 14.10.2009
comment
@sbi: 1) честно, я не использовал правильную фразу. 2) Знаю, знаю, ты сейчас придираешься. 3) не цитирую Страустропа, но я пытаюсь передать тот же смысл. В любом случае спасибо за -1 - person sbk; 14.10.2009
comment
И для придирки: следует отметить, что временное относится к большему количеству вещей. Например, вы можете поймать временные объекты (объекты исключения - это временные) по неконстантной ссылке. Таким образом, только некоторые временные объекты - те, которые являются rvalue - не могут быть связаны неконстантными ссылками. - person Johannes Schaub - litb; 15.10.2009
comment
@sbk: Простите, если я вас обидел, но я не думаю, что 2) придирается к мелочам. Rvalue просто не являются константами, если вы не сделаете их такими и не можете изменить их, если они не являются встроенными. Мне потребовалось некоторое время, чтобы понять, делаю ли я что-то не так, например, в примере со строкой (JFTR: я не делаю этого), поэтому я склонен серьезно относиться к этому различию. - person sbi; 15.10.2009
comment
Согласен с sbi - это совсем не придирок. В основе семантики перемещения лежит то, что r-значения типа класса лучше всего сохранять неконстантными. - person Johannes Schaub - litb; 15.10.2009
comment
@sbi и litb: это придирки, потому что я рассмотрел ваши вопросы. Это прямо здесь, говоря, что игнорирование ссылок C ++ 0x rvalue, потому что они не имеют отношения к вопросу. И во-вторых, и я просто повторяюсь здесь, исходная идея состоит в том, что нет смысла изменять временные значения. Почему тогда разрешено вызывать неконстантный член - это другой вопрос, но я считаю, что это логический порядок вещей. Я был бы счастлив, если бы кто-нибудь указал мне на исторический документ, подтверждающий мою неправоту. (Без обид) - person sbk; 15.10.2009
comment
Я думаю, auto_ptr не сработал бы, если бы мы не могли вызывать неконстантные функции для rvalues ​​типа класса. auto_ptr основан на вызове operator auto_ptr_ref() с неконстантными r-значениями. - person Johannes Schaub - litb; 16.10.2009
comment
@sbk: Но есть смысл изменять неконстантные временные файлы. Код типа f(g()+=h()) не такой необычный и немыслимый - и часто такой код полагается на возможность вызывать неконстантные функции-члены. Мне очень жаль, но я все еще думаю, что ты ошибаешься в этом. Значения r, не относящиеся к POD, можно изменять, и это важная функция, которую большинство программистов принимают как должное, не задумываясь - и не только в C ++ 1x. - person sbi; 17.10.2009
comment
@sbi: Хорошо, тогда почему не разрешено связывать неконстантные временные объекты с неконстантными ссылками? - person sbk; 18.10.2009
comment
@sbk: Я уже написал, что я помню как причину: Причина, по которой Страуструп (в D&E) запрещает привязку rvalue к неконстантным ссылкам, заключается в том, что если g () Алексея изменит объект (что вы бы ожидать от функции, принимающей неконстантную ссылку), она изменит объект, который умирает, так что никто не сможет получить измененное значение в любом случае. Он говорит, что это, скорее всего, ошибка. IOW, причина аналогична причине, по которой был введен const: для предотвращения глупых ошибок. - person sbi; 18.10.2009
comment
@sbk, Ваше утверждение о том, что временные объекты живут до конца оператора, в котором они содержатся, будет означать, что следующий цикл for верен: for(char const *s = string().c_str();*s;s++) ;. Но это неправильно, потому что string () уничтожается не в конце оператора, а в конце инициализации s, что делает разыменование цикла не принадлежащей памяти. - person Johannes Schaub - litb; 27.11.2009
comment
Что касается X& x = getx().ref();, я считаю, что в настоящее время это дефект в Стандарте: похоже, не говорится, что только временные библиотеки, не связанные ссылками, еще имеют продленный срок службы. Фактически, буквально читая Стандарт, я обнаружил, что в нем говорится, что следующий код недействителен, если временные ссылки, связанные ссылками, затронуты этими правилами: struct A { A &a() { return *this; } int m; }; ... A().a().m потому что он говорит, что временная привязка к возвращаемому значению функции уничтожается, когда функция возвращается. *this относится к временному, и он привязан к возвращаемому значению. - person Johannes Schaub - litb; 27.11.2009
comment
Комитет C ++ решил, что нет смысла изменять временные библиотеки Неправильно. - person curiousguy; 07.12.2011
comment
Вы можете продолжать передавать его вечно, просто чтобы ваши манипуляции с объектом исчезли - вот контрпример: передача временного std::ofstream в writeto функцию. - person PBS; 19.09.2015
comment
@sbi ваш аргумент в пользу запрета передачи временных объектов функциям с неконстантными ссылочными параметрами lvalue убедителен: потому что мы не можем получить измененный объект, и этот объект используется для вывода. Однако можете ли вы объяснить, почему привязка временной ссылки к локальной неконстантной lvalue-ссылке недопустима и не продлевает время жизни временного объекта, как это делает ссылка на константу? почему myclass& ref = myclass{} не допускается в философии? Мы можем идентифицировать это временное время через ref. Очень признателен, если вы дадите мне любезное руководство и помощь! Искренне. - person Han XIAO; 14.07.2018
comment
@XIAO: Не знаю. IIRC, ссылки на константы, продлевающие срок службы временных библиотек, являются более поздним дополнением. Я не знаю, почему они также не разрешили неконстантные ссылки. - person sbi; 15.07.2018
comment
Это должно быть предупреждением, а не ошибкой (максимум!), Потому что передача параметра с помощью ref не означает, что я изменю его, может быть, я хотел бы вызвать его неконстантный метод, который изменяет другой объект, который он содержит, а именно общий. Это обычное дело для помощников осциллографа. Для меня это бесполезное ограничение. - person jenkas; 26.02.2020

В вашем коде getx() возвращает временный объект, так называемое «rvalue». Вы можете скопировать rvalue в объекты (также известные как переменные) или привязать их к константным ссылкам (что продлит их время жизни до конца жизни ссылки). Вы не можете привязать rvalue к неконстантным ссылкам.

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

g(getx()); // g() would modify an object without anyone being able to observe

Если вы хотите это сделать, вам нужно сначала создать локальную копию объекта или привязать его к константной ссылке:

X x1 = getx();
const X& x2 = getx(); // extend lifetime of temporary to lifetime of const reference

g(x1); // fine
g(x2); // can't bind a const reference to a non-const reference

Обратите внимание, что следующий стандарт C ++ будет включать ссылки на rvalue. Поэтому то, что вы называете ссылками, становится называться «ссылками lvalue». Вам будет разрешено связывать rvalue со ссылками на rvalue, и вы можете перегружать функции на "rvalue-ness":

void g(X&);   // #1, takes an ordinary (lvalue) reference
void g(X&&);  // #2, takes an rvalue reference

X x; 
g(x);      // calls #1
g(getx()); // calls #2
g(X());    // calls #2, too

Идея, лежащая в основе ссылок rvalue, заключается в том, что, поскольку эти объекты все равно умрут, вы можете воспользоваться этими знаниями и реализовать так называемую «семантику перемещения», определенный вид оптимизации:

class X {
  X(X&& rhs)
    : pimpl( rhs.pimpl ) // steal rhs' data...
  {
    rhs.pimpl = NULL; // ...and leave it empty, but deconstructible
  }

  data* pimpl; // you would use a smart ptr, of course
};


X x(getx()); // x will steal the rvalue's data, leaving the temporary object empty
person sbi    schedule 14.10.2009
comment
Привет, это отличный ответ. Необходимо знать одну вещь: g(getx()) не работает, потому что его подпись g(X& x), а get(x) возвращает временный объект, поэтому мы не можем привязать временный объект (rvalue) к непостоянной ссылке, верно? И в вашем первом фрагменте кода, я думаю, будет const X& x2 = getx(); вместо const X& x1 = getx(); .. - person SexyBeast; 08.11.2014
comment
Спасибо, что указали на эту ошибку в моем ответе через 5 лет после того, как я ее написал! :-/ Да, ваши рассуждения верны, хотя и немного наоборот: мы не можем привязать временные ссылки к ссылкам, отличным от const (lvalue), и поэтому временное значение, возвращаемое getx() (а не get(x)), не может быть привязано к ссылке lvalue, которая является аргументом для g() . - person sbi; 09.11.2014
comment
Ммм, что вы имели в виду под getx() (а не get(x))? - person SexyBeast; 09.11.2014
comment
Когда я пишу ... getx () (а не get (x)) ..., я имею в виду, что имя функции getx(), а не get(x) (как вы писали). - person sbi; 09.11.2014
comment
Этот ответ смешивает терминологию. Rvalue - это категория выражения. Временный объект - это объект. Rvalue может обозначать или не обозначать временный объект; а временный объект может быть обозначен или не обозначен rvalue. - person M.M; 08.05.2017

Вы показываете, что цепочка операторов разрешена.

 X& x = getx().ref(); // OK

Выражение - «getx (). Ref ();» и это выполняется до завершения перед присвоением 'x'.

Обратите внимание, что getx () возвращает не ссылку, а полностью сформированный объект в локальном контексте. Объект временный, но не const, что позволяет вызывать другие методы для вычисления значения или возникновения других побочных эффектов.

// It would allow things like this.
getPipeline().procInstr(1).procInstr(2).procInstr(3);

// or more commonly
std::cout << getManiplator() << 5;

Лучший пример этого можно найти в конце этого ответа

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

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

x& = const_cast<x&>(getX());

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

Итак, единственный оставшийся вопрос: почему стандарт не разрешает ссылку на временные объекты для продления жизни объекта за пределами конца оператора?

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

Подумайте об этой ситуации:

int getI() { return 5;}
int x& = getI();

x++; // Note x is an alias to a variable. What variable are you updating.

Продление срока службы этого временного объекта будет очень запутанным.
Пока следующее:

int const& y = getI();

Дает вам код, который интуитивно понятен и понятен.

Если вы хотите изменить значение, вы должны вернуть значение переменной. Если вы пытаетесь избежать затрат на копирование объекта обратно из функции (поскольку кажется, что объект скопирован обратно (технически это так)). Тогда не беспокойтесь, компилятор очень хорош в «Оптимизации возвращаемого значения»

person Martin York    schedule 14.10.2009
comment
Итак, единственный оставшийся вопрос: почему стандарт не разрешает ссылку на временные объекты для продления жизни объекта за пределами конца оператора? Вот и все! Вы поняли мой вопрос. Но я не согласен с вашим мнением. Вы говорите, что сделать компилятор очень сложно, но это было сделано для константной ссылки. Вы говорите в своем примере Note x - это псевдоним переменной. Какую переменную вы обновляете. Без проблем. Есть уникальная переменная (временная). Какой-то временный объект (равно 5) необходимо заменить. - person Alexey Malistov; 14.10.2009
comment
@Martin: Висячие ссылки не только неопрятны. Они могут привести к серьезным ошибкам при доступе к ним позже в этом методе! - person mmmmmmmm; 14.10.2009
comment
@Alexey: Обратите внимание, что привязка к константной ссылке увеличивает время жизни временного, это исключение, которое было добавлено намеренно (TTBOMK, чтобы разрешить ручную оптимизацию). Не было добавлено исключение для неконстантных ссылок, потому что связывание временной ссылки с неконстантной ссылкой, скорее всего, было ошибкой программиста. - person sbi; 14.10.2009
comment
@alexy: ссылка на невидимую переменную! Не то чтобы интуитивно. - person Martin York; 14.10.2009
comment
const_cast<x&>(getX()); не имеет смысла - person curiousguy; 07.12.2011
comment
@Loki: хранение ссылки на временные объекты внутри их объектов не продлевает время жизни даже с const ссылками. Так что это не может быть причиной. - person Ben Voigt; 28.02.2013
comment
@BenVoigt: Я думал, это то, что я только что сказал? Я не совсем уверен, о каком абзаце вы говорите, но полностью согласен с вашим утверждением. Если вы отредактируете вопрос, чтобы отметить то, что вы считаете неправильным / неясным, я обновлю и постараюсь уточнить. Спасибо. - person Martin York; 28.02.2013
comment
@Loki: Это был ваш последний предыдущий комментарий. Я не уверен, почему нерасширение временных файлов, связанных неавтоматическими, актуально, поскольку уже разрешено связывать ссылку на член const с ссылкой на параметр конструктора const, которая привязана к временной, и это создает проблему времени жизни. Добавление ссылок, отличных от const, не создаст новой проблемы в этом отношении, это просто расширит область действия существующего gotcha. - person Ben Voigt; 28.02.2013
comment
@BenVoigt: Дата: Oct 14 '09 at 18:04 - person Martin York; 28.02.2013
comment
Да, вот этот. Я полагаю, что если я наткнусь на ссылку из другого вопроса, другие пользователи тоже будут, и стоит быть точным даже в очень старых ответах, когда они связаны. - person Ben Voigt; 28.02.2013
comment
OK. Удалено. Полагаю, плохое предположение о причине. Комментарий SBI - это оправдание, которое я должен был дать. - person Martin York; 28.02.2013
comment
отличный ответ, но мне было интересно, что делает реализация, чтобы продлить срок службы константной эталонной ситуации, скопировать данные в кучу? - person Bin; 07.04.2016

Почему обсуждается в C ++ FAQ (у меня выделено жирным шрифтом):

В C ++ неконстантные ссылки могут связываться с lvalue, а константные ссылки могут связываться с lvalue или rvalue, но нет ничего, что могло бы связываться с неконстантным rvalue. Это для защиты людей от изменения значений временных файлов, которые уничтожаются до того, как их новое значение может быть использовано. Например:

void incr(int& a) { ++a; }
int i = 0;
incr(i);    // i becomes 1
incr(0);    // error: 0 is not an lvalue

Если бы этот incr (0) был разрешен, либо какое-то временное, которое никто никогда не видел, было бы увеличено или, что еще хуже, значение 0 стало бы 1. Последнее звучит глупо, но на самом деле была такая ошибка в ранних компиляторах Fortran, которые устанавливали зарезервировать место в памяти для хранения значения 0.

person Tony Delroy    schedule 30.12.2015
comment
Было бы забавно увидеть лицо программиста, укусившего эту нулевую ошибку Fortran! x * 0 дает x? Какие? Какие?? - person John D; 15.05.2016
comment
Последний аргумент особенно слаб. Ни один из заслуживающих упоминания компиляторов никогда не изменит значение от 0 до 1 или даже не интерпретирует incr(0); таким образом. Очевидно, если бы это было разрешено, это было бы интерпретировано как создание временного целого числа и передача его incr() - person user3204459; 22.03.2017

Основная проблема в том, что

g(getx()); //error

является логической ошибкой: g изменяет результат getx(), но у вас нет возможности изучить измененный объект. Если g не нужно было изменять свой параметр, тогда ему не потребовалась бы ссылка lvalue, он мог бы взять параметр по значению или по константной ссылке.

const X& x = getx(); // OK

допустимо, потому что вам иногда нужно повторно использовать результат выражения, и совершенно очевидно, что вы имеете дело с временным объектом.

Однако сделать это невозможно.

X& x = getx(); // error

действителен, не делая g(getx()) действительным, чего разработчики языка пытались избежать в первую очередь.

g(getx().ref()); //OK

допустимо, потому что методы знают только о константе this, они не знают, вызываются ли они по lvalue или по rvalue.

Как всегда в C ++, у вас есть обходной путь для этого правила, но вы должны сообщить компилятору, что вы знаете, что делаете, явным образом:

g(const_cast<x&>(getX()));
person Dan Berindei    schedule 13.11.2009

Похоже, что на исходный вопрос о том, почему это запрещено, был дан четкий ответ: "потому что это, скорее всего, ошибка".

FWIW, я подумал, что покажу как это можно сделать, хотя я не думаю, что это хорошая техника.

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

// Assuming: void Person::GetNameAndAddr(std::string &name, std::string &addr);
string name;
person.GetNameAndAddr(name, string()); // don't care about addr

Как объяснялось в предыдущих ответах, это не компилируется. Но это компилируется и работает правильно (с моим компилятором):

person.GetNameAndAddr(name,
    const_cast<string &>(static_cast<const string &>(string())));

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

string name;
string unused;
person.GetNameAndAddr(name, unused); // don't care about addr

Этот метод действительно вводит ненужную локальную переменную в область действия метода. Если по какой-то причине вы хотите предотвратить его использование позже в методе, например, чтобы избежать путаницы или ошибки, вы можете скрыть его в локальном блоке:

string name;
{
    string unused;
    person.GetNameAndAddr(name, unused); // don't care about addr
}

- Крис

person Chris Pearson    schedule 22.02.2013

Зачем вам когда-нибудь нужен X& x = getx();? Просто используйте X x = getx(); и положитесь на RVO.

person DevFred    schedule 14.10.2009
comment
Потому что я хочу позвонить g(getx()), а не g(getx().ref()) - person Alexey Malistov; 14.10.2009
comment
@ Алексей, это не настоящая причина. Если вы сделаете это, то где-то у вас будет логическая ошибка, потому что g собирается изменить что-то, что вы больше не можете достать. - person Johannes Schaub - litb; 15.10.2009
comment
@ JohannesSchaub-litb, может, ему все равно. - person curiousguy; 07.12.2011
comment
полагаться на RVO, за исключением того, что он не называется RVO. - person curiousguy; 07.12.2011
comment
@curiousguy: Это очень приемлемый термин для этого. Нет ничего плохого в том, чтобы называть его RVO. - person Puppy; 07.12.2011

Злой обходной путь включает ключевое слово «изменяемый». На самом деле быть злом - это упражнение для читателя. Или посмотрите здесь: http://www.ddj.com/cpp/184403758

person paul    schedule 14.10.2009

Отличный вопрос, и вот моя попытка дать более лаконичный ответ (поскольку много полезной информации находится в комментариях, и ее трудно раскопать в шуме).

Любая ссылка, привязанная напрямую к временному объекту, продлит его жизнь [12.2.5]. С другой стороны, ссылка, инициализированная другой ссылкой, не (даже если она в конечном итоге такая же временная). Это имеет смысл (компилятор не знает, к чему в конечном итоге относится эта ссылка).

Но вся эта идея крайне сбивает с толку. Например. const X &x = X(); сделает временное продолжением до тех пор, пока указана x ссылка, но const X &x = X().ref(); НЕ будет (кто знает, что ref() фактически вернуло). В последнем случае деструктор для X вызывается в конце этой строки. (Это можно наблюдать с помощью нетривиального деструктора.)

В общем, это кажется запутанным и опасным (зачем усложнять правила, касающиеся времени жизни объектов?), Но, по-видимому, была потребность, по крайней мере, в константных ссылках, поэтому стандарт устанавливает для них такое поведение.

[Из комментария sbi]: обратите внимание, что тот факт, что привязка его к ссылке const увеличивает время жизни временного, является исключением, которое было добавлен намеренно (TTBOMK для возможности ручной оптимизации). Не было добавлено исключение для неконстантных ссылок, потому что связывание временной ссылки с неконстантной ссылкой, скорее всего, было ошибкой программиста.

Все временные файлы сохраняются до конца полного выражения. Однако, чтобы использовать их, вам понадобится трюк, как в случае с ref(). Это законно. Кажется, нет веских причин для перепрыгивания дополнительных пялец, кроме как напомнить программисту о том, что происходит что-то необычное (а именно, ссылочный параметр, изменения которого будут быстро потеряны).

[Еще один комментарий sbi] Причина, по которой Страуструп (в D&E) запрещает привязку значений rvalue к неконстантным ссылкам, заключается в том, что , если g () Алексея изменит объект (чего можно ожидать от функции, принимающей неконстантную ссылку), он изменит объект, который умирает, так что в любом случае никто не сможет получить измененное значение. Он говорит, что это, скорее всего, ошибка.

person DS.    schedule 08.04.2012

«Ясно, что временный объект не является постоянным в приведенном выше примере, потому что разрешены вызовы непостоянных функций. Например, ref () может изменить временный объект».

В вашем примере getX () не возвращает const X, поэтому вы можете вызвать ref () почти так же, как вы могли бы вызвать X (). Ref (). Вы возвращаете неконстантную ссылку и поэтому можете вызывать неконстантные методы, чего вы не можете сделать, так это присвоить ссылку неконстантной ссылке.

Наряду с комментарием SadSidos это делает ваши три пункта неверными.

person Patrick    schedule 14.10.2009

У меня есть сценарий, которым я хотел бы поделиться, где я хотел бы сделать то, о чем просит Алексей. В подключаемом модуле Maya C ++ я должен проделать следующую махинацию, чтобы получить значение в атрибуте узла:

MFnDoubleArrayData myArrayData;
MObject myArrayObj = myArrayData.create(myArray);   
MPlug myPlug = myNode.findPlug(attributeName);
myPlug.setValue(myArrayObj);

Это утомительно писать, поэтому я написал следующие вспомогательные функции:

MPlug operator | (MFnDependencyNode& node, MObject& attribute){
    MStatus status;
    MPlug returnValue = node.findPlug(attribute, &status);
    return returnValue;
}

void operator << (MPlug& plug, MDoubleArray& doubleArray){
    MStatus status;
    MFnDoubleArrayData doubleArrayData;
    MObject doubleArrayObject = doubleArrayData.create(doubleArray, &status);
    status = plug.setValue(doubleArrayObject);
}

И теперь я могу написать код с начала поста как:

(myNode | attributeName) << myArray;

Проблема в том, что он не компилируется вне Visual C ++, потому что пытается привязать временную переменную, возвращаемую из | на ссылку MPlug оператора ‹*. Я хотел бы, чтобы это была ссылка, потому что этот код вызывается много раз, и я бы предпочел, чтобы MPlug не копировался так часто. Мне нужно, чтобы временный объект жил до конца второй функции.

Что ж, это мой сценарий. Просто подумал, что покажу пример, где хочется сделать то, что описывает Алексей. Я приветствую любую критику и предложения!

Спасибо.

person Robert Trussardi    schedule 22.06.2013