Почему нельзя получить неконстантную ссылку на временный объект, который getx()
возвращает функция ? Понятно, что это запрещено C ++ Standard, но меня интересует цель такого ограничения, а не ссылка на стандарт.
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;
}
- Ясно, что время жизни объекта не может быть причиной, потому что постоянная ссылка на объект не запрещена стандартом C ++.
- Понятно, что временный объект не является константой в приведенном выше примере, поскольку разрешены вызовы непостоянных функций. Например,
ref()
может изменить временный объект. - Кроме того,
ref()
позволяет обмануть компилятор и получить ссылку на этот временный объект, и это решает нашу проблему.
К тому же:
Они говорят: «Присвоение временного объекта константной ссылке увеличивает время жизни этого объекта» и «Ничего не сказано о неконстантных ссылках, хотя». Мой дополнительный вопрос . Продлевает ли следующее назначение время жизни временного объекта?
X& x = getx().ref(); // OK
Ответы:
Из этой статьи блога Visual C ++ о ссылках rvalue :
По сути, вы не должны пытаться модифицировать временные объекты по той самой причине, что они являются временными объектами и теперь умирают в любой момент. Причина, по которой вам разрешено вызывать неконстантные методы, заключается в том, что вы можете делать некоторые «глупые» вещи, если вы знаете, что делаете, и вы явно об этом (например, используете reinterpret_cast). Но если вы привязываете временную ссылку к неконстантной ссылке, вы можете продолжать передавать ее «навсегда», просто чтобы ваши манипуляции с объектом исчезли, потому что где-то по пути вы полностью забыли, что это было временным.
Если бы я был тобой, я бы переосмыслил дизайн своих функций. Почему g () принимает ссылку, изменяет ли она параметр? Если нет, сделайте это константной ссылкой, если да, почему вы пытаетесь передать ему временный текст, не волнует ли это, что вы изменяете временный код? Почему getx () все равно возвращается временно? Если вы поделитесь с нами своим реальным сценарием и тем, что вы пытаетесь достичь, вы можете получить несколько хороших советов о том, как это сделать.
Идти против языка и дурачить компилятор редко решает проблемы - обычно это создает проблемы.
Изменить: Отвечая на вопросы в комментарии: 1)
X& x = getx().ref(); // OK when will x die?
- Я не знаю, и мне все равно, потому что это именно то, что я имею в виду под "идти против языка". Язык говорит: «Временные символы умирают в конце утверждения, если они не связаны с константной ссылкой, и в этом случае они умирают, когда ссылка выходит из области видимости». Применяя это правило, кажется, что x уже мертв в начале следующего оператора, так как он не связан с константной ссылкой (компилятор не знает, что возвращает ref ()). Это только предположение однако.2) Я четко сформулировал цель: вам не разрешено изменять временные данные, потому что это просто не имеет смысла (игнорирование ссылок на значения C ++ 0x). Вопрос "тогда почему я могу звонить неконстантным участникам?" хороший, но у меня нет лучшего ответа, чем тот, который я уже сказал выше.
3) Ну, если я прав насчет х в
X& x = getx().ref();
смерти в конце утверждения, проблемы очевидны.В любом случае, основываясь на вашем вопросе и комментариях, я не думаю, что даже эти дополнительные ответы вас удовлетворит. Вот последняя попытка / краткое изложение: Комитет C ++ решил, что не имеет смысла изменять временные данные, поэтому они запретили привязку к неконстантным ссылкам. Может быть, некоторые реализации компилятора или исторические проблемы также были вовлечены, я не знаю. Затем возник какой-то конкретный случай, и было решено, что, несмотря ни на что, они по-прежнему допускают прямое изменение посредством вызова неконстантного метода. Но это исключение - вы, как правило, не можете изменять временные данные. Да, C ++ часто такой странный.
источник
int
т.д.), но это допускается для определяемых пользователем типов:(std::string("A")+"B").append("C")
.g()
будет изменен объект (что можно ожидать от функции, использующей неконстантную ссылку), это изменило бы объект, который собирается умереть, так что никто не мог бы получить измененное значение в любом случае. Он говорит, что это, скорее всего, ошибка.В ваш код
getx()
возвращается временный объект, так называемое «rvalue». Вы можете скопировать значения r в объекты (так называемые переменные) или связать их с константными ссылками (что продлит их время жизни до конца жизни ссылки). Вы не можете привязать значения к неконстантным ссылкам.Это было преднамеренное проектное решение, чтобы предотвратить случайное изменение пользователями объекта, который погибнет в конце выражения:
Если вы хотите сделать это, вам сначала нужно будет сделать локальную копию или объект или связать его с константной ссылкой:
Обратите внимание, что следующий стандарт C ++ будет включать ссылки на rvalue. Поэтому то, что вы знаете как ссылки, становится так называемым «ссылками lvalue». Вам будет разрешено связывать rvalue со ссылками на rvalue, и вы можете перегрузить функции на "rvalue-ness":
Идея, стоящая за ссылками на rvalue, заключается в том, что, поскольку эти объекты все равно умрут, вы можете воспользоваться этими знаниями и реализовать так называемую «семантику перемещения», определенный вид оптимизации:
источник
g(getx())
она не работает, потому что ее подпись естьg(X& x)
иget(x)
возвращает временный объект, поэтому мы не можем связать временный объект ( rvalue ) с непостоянной ссылкой, правильно? И в вашем первом фрагменте кода, я думаю, что это будетconst X& x2 = getx();
вместоconst X& x1 = getx();
..:-/
Да, ваши рассуждения верны, хотя и немного задом наперед: мы не можем привязать временныеconst
ссылки к ссылкам, не являющимся (lvalue), и поэтому временное значение, возвращаемоеgetx()
(и неget(x)
), не может быть привязано к ссылке lvalue, аргументом которой являетсяg()
.getx()
(и неget(x)
) ?getx()
, а неget(x)
(как вы написали).То, что вы показываете, это то, что оператор цепочки разрешен.
Выражение 'getx (). Ref ();' и это выполняется до завершения перед присваиванием 'x'.
Обратите внимание, что getx () не возвращает ссылку, а полностью сформированный объект в локальный контекст. Объект является временным, но он не является константным, что позволяет вам вызывать другие методы для вычисления значения или возникновения других побочных эффектов.
Посмотрите на конец этого ответа для лучшего примера этого
Вы не можете привязать временную ссылку к ссылке, потому что при этом будет сгенерирована ссылка на объект, который будет уничтожен в конце выражения, что оставит вас с висячей ссылкой (что неопрятно, а стандарт не любит неопрятность).
Значение, возвращаемое ref (), является допустимой ссылкой, но метод не обращает никакого внимания на срок службы возвращаемого объекта (поскольку он не может иметь эту информацию в своем контексте). Вы в основном только что сделали эквивалент:
Причина, по которой это можно сделать с помощью константной ссылки на временный объект, заключается в том, что стандарт продлевает срок службы временного элемента до срока службы ссылки, поэтому срок службы временных объектов продлевается до конца оператора.
Таким образом, единственный оставшийся вопрос заключается в том, почему стандарт не хочет разрешать использование временных ссылок для продления срока службы объекта до конца утверждения?
Я полагаю, что это потому, что из-за этого компилятору будет очень трудно получить правильные значения для временных объектов. Это было сделано для постоянных ссылок на временные файлы, так как это ограничивало использование и, таким образом, заставляло вас делать копию объекта, чтобы делать что-нибудь полезное, но предоставляет некоторые ограниченные функциональные возможности.
Подумайте об этой ситуации:
Продление срока службы этого временного объекта будет очень запутанным.
Пока следующее:
Даст вам код, который будет интуитивно понятен в использовании и понимании.
Если вы хотите изменить значение, вы должны возвращать значение в переменную. Если вы пытаетесь избежать затрат на копирование объекта обратно из функции (так как кажется, что объект является копией, созданной обратно (технически это так)). Тогда не беспокойтесь, компилятор очень хорош в «Оптимизации возвращаемого значения»
источник
const_cast<x&>(getX());
не имеет смыслаПочему это обсуждается в C ++ FAQ ( жирный шрифт ):
источник
x * 0
даетx
? Какой? Какой??incr(0);
таким образом. Очевидно, что если бы это было разрешено, это было бы интерпретировано как создание временного целого числа и передача его пользователюincr()
Основная проблема заключается в том, что
является логической ошибкой: изменяет
g
результат,getx()
но у вас нет шансов проверить измененный объект. Еслиg
бы не нужно было изменять его параметр, тогда не потребовалась бы ссылка на lvalue, он мог бы взять параметр по значению или по константной ссылке.допустимо, потому что иногда вам нужно повторно использовать результат выражения, и совершенно ясно, что вы имеете дело с временным объектом.
Однако это не возможно сделать
допустимо, не делая
g(getx())
валидным, чего и пытались избежать дизайнеры языка.допустимо, потому что методы знают только о константе
this
, они не знают, вызваны ли они на lvalue или на rvalue.Как всегда в C ++, у вас есть обходной путь для этого правила, но вы должны сообщить компилятору, что вы знаете, что делаете, будучи явным:
источник
Похоже, на первоначальный вопрос о том, почему это не разрешено, дан четкий ответ: «потому что это, скорее всего, ошибка».
FWIW, я думал, что покажу, как это можно сделать, хотя я не думаю, что это хорошая техника.
Причина, по которой я иногда хочу передать временное значение методу, принимающему неконстантную ссылку, заключается в том, что он намеренно отбрасывает значение, возвращаемое по ссылке, о которой вызывающий метод не заботится. Что-то вроде этого:
Как объяснялось в предыдущих ответах, это не компилируется. Но это компилируется и работает правильно (с моим компилятором):
Это просто показывает, что вы можете использовать приведение, чтобы лгать компилятору. Очевидно, было бы намного понятнее объявить и передать неиспользованную автоматическую переменную:
Этот метод вводит ненужную локальную переменную в область действия метода. Если по какой-то причине вы хотите предотвратить его использование позже в методе, например, чтобы избежать путаницы или ошибки, вы можете скрыть это в локальном блоке:
Крис
источник
Зачем тебе это
X& x = getx();
? Просто используйтеX x = getx();
и положитесь на RVO.источник
g(getx())
а неg(getx().ref())
g
она собирается изменить что-то, что вы больше не сможете получить.Злой обходной путь включает в себя ключевое слово «изменяемый». На самом деле быть злом - это упражнение для читателя. Или смотрите здесь: http://www.ddj.com/cpp/184403758
источник
Отличный вопрос, и вот моя попытка получить более краткий ответ (так как много полезной информации есть в комментариях и трудно разобраться в шуме.)
Любая ссылка, связанная непосредственно с временным, продлит его жизнь [12.2.5]. С другой стороны, ссылка, инициализированная с другой ссылкой, не будет (даже если она в конечном итоге будет такой же временной). Это имеет смысл (компилятор не знает, на что в конечном счете ссылается эта ссылка).
Но вся эта идея чрезвычайно запутана. Например
const X &x = X();
, временнаяx
ссылкаconst X &x = X().ref();
будет длиться столько же, сколько ссылка, но НЕ (кто знает, что наref()
самом деле вернулось). В последнем случае деструктор дляX
вызывается в конце этой строки. (Это можно наблюдать с помощью нетривиального деструктора.)Таким образом, это кажется в целом сбивающим с толку и опасным (зачем усложнять правила относительно времени жизни объекта?), Но, по-видимому, была необходимость, по крайней мере, для ссылок на const, поэтому стандарт устанавливает для них такое поведение.
Все временные сохраняются до конца полного выражения. Однако, чтобы использовать их, вам нужен такой же трюк, как и у вас
ref()
. Это законно. Кажется, нет веской причины для перехода через лишний обруч, кроме как для напоминания программисту о том, что происходит что-то необычное (а именно, эталонный параметр, модификации которого будут быстро потеряны).источник
«Ясно, что временный объект не является константой в приведенном выше примере, потому что разрешены вызовы неконстантных функций. Например, ref () может изменить временный объект».
В вашем примере getX () не возвращает const X, поэтому вы можете вызывать ref () почти так же, как вы можете вызвать X (). Ref (). Вы возвращаете неконстантную ссылку и поэтому можете вызывать неконстантные методы, но вы не можете назначить ссылку на неконстантную ссылку.
Наряду с комментарием SadSidos это делает ваши три пункта неверными.
источник
У меня есть сценарий, которым я хотел бы поделиться, где я хотел бы сделать то, что просит Алексей. В плагине Maya C ++ я должен выполнить следующий шенаниган, чтобы получить значение в атрибуте узла:
Написание этого утомительно, поэтому я написал следующие вспомогательные функции:
И теперь я могу написать код с начала поста как:
Проблема в том, что он не компилируется вне Visual C ++, потому что он пытается связать временную переменную, возвращаемую из | оператор на ссылку MPlug оператора <<. Я хотел бы, чтобы это было ссылкой, потому что этот код вызывается много раз, и я бы предпочел, чтобы MPlug не копировался так много. Мне нужен только временный объект, чтобы жить до конца второй функции.
Ну, это мой сценарий. Просто подумал, что покажу пример, где хочется делать то, что описывает Алексей. Я приветствую все критические замечания и предложения!
Спасибо.
источник