Почему взятие адреса временного незаконно?

Я знаю, что код, написанный ниже, незаконен

void doSomething(std::string *s){}
int main()
{
     doSomething(&std::string("Hello World"));
     return 0;
}

Причина в том, что нам не разрешено брать адрес временного объекта. Но мой вопрос: ПОЧЕМУ?

Рассмотрим следующий код

class empty{};
int main()
{
      empty x = empty(); //most compilers would elide the temporary
      return 0;
}

В принятом ответе здесь упоминается

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

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

Противоречит ли это тому, что адрес временного брать нельзя?

Я также хотел бы знать, как реализована оптимизация возвращаемого значения. Может ли кто-нибудь предоставить ссылку или статью, связанную с реализацией RVO?


person Prasoon Saurav    schedule 29.11.2010    source источник
comment
Попробуйте это: &(std::string()=std::string("Hello World"));   -  person curiousguy    schedule 03.12.2011


Ответы (7)


&std::string("Hello World")

Проблема не в том, что std::string("Hello World") дает временный объект. Проблема в том, что выражение std::string("Hello World") является выражением rvalue, которое ссылается на временный объект.

Вы не можете взять адрес rvalue, потому что не все rvalue имеют адреса (и не все rvalue являются объектами). Рассмотрим следующее:

42

Это целочисленный литерал, который является первичным выражением и значением r. Это не объект, и у него (скорее всего) нет адреса. &42 бессмысленно.

Да, rvalue может ссылаться на объект, как в первом примере. Проблема в том, что не все rvalue относятся к объектам.

person James McNellis    schedule 29.11.2010
comment
@Rambo: временный объект должен иметь адрес, потому что объект по определению является областью хранения, а хранилище, занимаемое объектом, должно быть адресуемым. Проблема в том, что на временный объект ссылается выражение rvalue, но не все выражения rvalue ссылаются на объекты. 42 не является временным объектом; это целочисленный литерал. - person James McNellis; 29.11.2010
comment
Число 42 является объектом и имеет тип int. Он может быть связан с константной ссылкой, и если это так, его адрес может быть взят. Компилятор всегда пытается исключить адреса, которые не оцениваются (то есть поднимают значения в регистры), независимо от того, является ли тип объекта примитивным или нет. - person Potatoswatter; 29.11.2010
comment
@Potatoswatter: 42 - это не объект, это значение (и имеет тип int). Если он привязан к константной ссылке (т. е. const int& x = 42;), то создается временный объект со значением 42, и ссылка привязывается к этому временному объекту. (FWIW, я подсознательно использовал термин rvalue вместо временного, забыв, что временный объект, к которому была привязана ссылка, все еще является временным объектом; я добавлю примечание об этом...) - person James McNellis; 29.11.2010
comment
Это не объект, если он не используется как таковой. Но если он используется как объект, создается объект. &42 точно так же бессмысленно, как & static_cast<int const &>( 42 ). Что все еще несколько бессмысленно, но не совсем так. Смотрите мой ответ. - person Potatoswatter; 29.11.2010
comment
Не все rvalue ссылаются на объекты/имеют адрес, но все rvalue temporaries имеют адрес/ссылку на объекты. имя temporary классифицирует их время жизни, которое является временным. если нет объекта, нет ничего временного. - person Johannes Schaub - litb; 05.12.2011
comment
42 хранится где-то в памяти или объектном коде, не так ли? Если это так, то почему использование адреса 42 с &(42) не имеет смысла? - person Michael Gazonda; 22.08.2014
comment
@Michael Gazonda Извините за некромантию, но: на реальном компьютере многие данные могут никогда не появляться в адресуемой оперативной памяти, а только в регистрах. Другие данные, такие как целочисленные литералы, могут дополнительно отображаться как часть операции машинного кода, но это не похоже на то, что С++ позволяет вам использовать адреса для этого (вне точек входа функции) - person hegel5000; 16.07.2020

Длинный ответ:

[...] можно сделать вывод, что временное присутствовало в каком-то месте памяти

По определению:

  • временно означает: временный объект
  • объект занимает область хранения
  • все объекты имеют адрес

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

OTOH, вы не просто получаете адрес, вы используете встроенный оператор адреса. В спецификации встроенного оператора address-of указано, что у вас должно быть lvalue:

  • &std::string() имеет неправильный формат, поскольку std::string() является значением r. Во время выполнения эта оценка этого выражения создает временный объект в качестве побочного эффекта, а выражение дает значение r, которое ссылается на созданный объект.
  • &(std::string() = "Hello World") имеет правильный формат, поскольку std::string() = "Hello World" является lvalue. По определению lvalue относится к объекту. Объект, на который ссылается это lvalue, является точно таким же временным

Короткий ответ:

Это правило. Ему не нужны (неправильные, необоснованные) оправдания, которые придумывают некоторые люди.

person curiousguy    schedule 05.12.2011
comment
Это показывает, что значение R может быть слева от присваивания. Бессмысленно... но факт!!! - person GetFree; 15.04.2017

$5.3.1/2 - "Результатом унарного оператора & является указатель на его операнд. Операнд должен быть lvalue или квалифицированным идентификатором."

Такие выражения, как

99

A() // where A is a user defined class with an accessible 
    // and unambiguous default constructor

все значения R.

$3.10/2 — «lvalue относится к объекту или функции. Некоторые выражения rvalue — относящиеся к типу class или cv-qualified class — также относятся к объектам. 47)»

И это мое предположение: хотя значения R могут занимать память (например, в случае объектов), стандарт С++ не позволяет использовать их адрес для обеспечения единообразия со встроенными типами.

Хотя вот что интересно:

void f(const double &dbl){
   cout << &dbl;
}

int main(){
   f(42);
}

Выражение «42» — это значение R, которое связано со «ссылкой на const double» и, следовательно, создает временный объект типа double. Адрес этого временного объекта можно взять внутри функции 'f'. Но обратите внимание, что внутри 'f' это на самом деле не временное или Rvalue. В тот момент, когда ему присваивается имя, такое как «dbl», оно обрабатывается как выражение Lvalue внутри «f».

Вот кое-что о NRVO (похожее)

person Chubsdad    schedule 29.11.2010
comment
Но обратите внимание, что внутри 'f' это не совсем временный Конечно, это так: ссылка привязана к временному объекту (безымянному объекту), созданному f(42). Вы можете сказать, что dbl называет объект внутри f, но объект все еще существует (только что) после возврата функции, и в этот момент у него нет имени. - person curiousguy; 05.12.2011

Временное значение является примером "rvalue" C++. Предполагается, что он просто представляет значение внутри своего типа. Например, если вы напишете 42 в двух разных местах вашей программы, экземпляры 42 будут неразличимы, несмотря на то, что они, вероятно, находятся в разных местах в разное время. Причина, по которой вы не можете взять адрес, заключается в том, что вам нужно что-то сделать, чтобы указать, что адрес должен быть, потому что в противном случае концепция адреса будет семантически нечистой и неинтуитивной.

Языковое требование «сделать что-то» несколько произвольно, но оно делает программы на C++ чище. Было бы отстойно, если бы у людей появилась привычка брать адреса временных сотрудников. Понятие адреса тесно связано с понятием времени жизни, поэтому имеет смысл сделать так, чтобы «мгновенные» значения не имели адресов. Тем не менее, если вы будете осторожны, вы можете получить адрес и использовать его в течение времени жизни, которое позволяет стандарт.

В других ответах здесь есть некоторые заблуждения:

  • «Вы не можете взять адрес rvalue, потому что не все rvalue имеют адреса». — Не все lvalue также имеют адреса. Типичной локальной переменной типа int, которая участвует в простом цикле и впоследствии не используется, скорее всего, будет назначен регистр, но не место в стеке. Отсутствие ячейки памяти означает отсутствие адреса. Однако компилятор назначит место в памяти, если вы возьмете его адрес. То же самое относится и к rvalue, которые могут быть привязаны к const ссылкам. «Адрес 42» может быть получен как таковой:

    int const *fortytwo_p = & static_cast<int const &>( 42 );
    

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

    Стоит отметить, что C++0x очищает концепции, определяя prvalue как значение выражения, независимое от хранилища, а glvalue как место хранения. независимо от его содержания. Это, вероятно, и было целью стандарта C++03 в первую очередь.

  • «Тогда вы можете изменить временное, что бессмысленно». — На самом деле временки с побочными эффектами полезно модифицировать. Учти это:

    if ( istringstream( "42" ) >> my_int )
    

    Это хорошая идиома для преобразования числа и проверки успешности преобразования. Он включает в себя создание временного объекта, вызов для него изменяющей функции и последующее его уничтожение. Далеко не бессмысленно.

person Potatoswatter    schedule 29.11.2010
comment
Я не согласен с первым пунктом. Да, если вы не берете адрес объекта, компилятор может выполнить оптимизацию так, что у него больше не будет адреса. Это не имеет значения, потому что каждое lvalue относится к объекту, поэтому всегда можно взять адрес lvalue (lvalue может также относиться к функции, но я не думаю, что это важно для этого). То же самое не относится к rvalue: невозможно взять адрес rvalue 42, потому что это не объект. (static_cast<int const &>( 42 ) создает временный объект со значением 42; 42 не является объектом) - person James McNellis; 29.11.2010
comment
@James: компилятору разрешено создавать временные файлы для любых случайных целей. Где в стандарте говорится, что временное значение инициализируется значением rvalue 42, которое ранее не было временным? — Кроме того, это немного не по теме, поскольку ОП спрашивал о временном std::string, который в любом случае является объектом. - person Potatoswatter; 29.11.2010
comment
3.10/2 утверждает, что 42 не является объектом. 8.5.3/5 (предпоследний маркер, заканчивающийся сноской 93) утверждает, что для инициализации ссылки создается временный объект. Причина, по которой это относится к теме, заключается в следующем: вы можете взять только адрес объекта. Все lvalue относятся к объектам, поэтому вы можете применить & ко всем lvalue. То же самое неверно для rvalue: некоторые выражения rvalue не относятся к объектам, поэтому вы не можете применить & to all rvalues. 42` — это просто пример rvalue, который не относится к объекту. - person James McNellis; 29.11.2010
comment
@James: 3.10/2 говорит, что std::string( "hello" ) является объектом, но не говорит однозначно, что 42 таковым не является. В любом случае, я считаю, что вы правы с точки зрения языковой семантики. Что касается применения &, для этого просто требуется lvalue; rvalue с базовыми объектами не разрешены. Это ломает тавтологию. В теории и на практике адрес появляется, когда и когда вы этого хотите, что означает, так или иначе, получение lvalue из выражения. То, что вы не начинаете с lvalue, не означает, что вы не можете получить его в конце. - person Potatoswatter; 29.11.2010
comment
Временное значение — это пример rvalue C++. Это утверждение не имеет смысла: временное означает временный объект; rvalue означает rvalue выражение. Выражения существуют во время компиляции, а объекты существуют во время выполнения. - person curiousguy; 05.12.2011
comment
@ Любопытно, что в спецификации временный xan может быть атрибутом выражения, а также атрибутом объекта. выражение является временным, если оно напрямую ссылается на временный объект. это очень похоже на понятие lvalue битового поля. - person Johannes Schaub - litb; 05.12.2011

Его можно взять, но как только временное перестанет существовать, у вас останется болтающийся указатель.

ИЗМЕНИТЬ

Для минусующих:

const std::string &s = std::string("h");
&s;

является законным. s является ссылкой на временный. Следовательно, можно взять временный адрес объекта.

ИЗМЕНИТЬ2

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

Вопрос ОП касается временных значений (с точки зрения слов, которые он использует), а его пример касается значений r. Это два разных понятия.

person lijie    schedule 29.11.2010
comment
Операнд встроенного оператора адреса (унарный &) должен быть lvalue. - person James McNellis; 29.11.2010
comment
операнд оператора address-of должен быть lvalue, но временный может иметь свой адрес, потому что выражения не являются l/rvalues, а не объектами. - person lijie; 29.11.2010
comment
Выражения не являются lvalue и не могут иметь свои адреса. Ваш пример не имеет значения, поскольку s не является временным. - person user207421; 29.11.2010
comment
Вы хорошо заметили, что на временный объект, к которому привязана ссылка const, можно ссылаться через выражение lvalue. Я не думаю, что это то, что означает ОП, но это, безусловно, верный момент. - person James McNellis; 29.11.2010
comment
@EJP: s не временное, нет. s — это ссылка, привязанная к временному объекту, поэтому s обозначает временный объект. - person James McNellis; 29.11.2010
comment
@James: я прочитал здесь, что временные файлы, однако, могут быть связаны с постоянными ссылками, и это увеличивает время жизни временного объекта. Однако, поскольку s является ссылкой, а все ссылки являются lvalue, как говорит AndreyT в своем посте, мы можем взять его адрес. - person Prasoon Saurav; 29.11.2010
comment
@EJP: s - это ссылка, означает, что это псевдоним, означает, что после привязки он так же хорош, как и временный, к которому он привязан. - person lijie; 29.11.2010
comment
@James: да, я не знаю, является ли вопрос неправильным; он приписывает незаконность кода невозможности получить адрес временного пользователя, что не соответствует действительности. ну, весь вопрос как бы предполагает, что временное значение является синонимом rvalue. - person lijie; 29.11.2010
comment
@lijie: также прочитайте мой вопрос "> stackoverflow.com/questions/2145030/ - person Prasoon Saurav; 29.11.2010
comment
Опять же, совершенно правильный ответ отвергается. Это переполнение стека. - person curiousguy; 03.12.2011

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

Приведенная вами цитата не об этой ситуации, это конкретная оптимизация, разрешенная в деклараторах с инициализаторами.

person user207421    schedule 29.11.2010
comment
@downvoter, пожалуйста, объясни. Все остальные правильные ответы говорят почти то же самое, хотя и более техническим языком. - person user207421; 05.12.2011
comment
@curiousguy Очень мило, что это? Похоже, он создает анонимный объект, который содержит временную копию на RHS. - person user207421; 06.12.2011
comment
Он создает временный объект, а затем присваивает ему значение другого временного объекта. Это юридический код, показывающий, что C++ не запрещает запись во временный объект. - person curiousguy; 06.12.2011
comment
@curiousguy Это юридический код, показывающий, что C++ не запрещает инициализировать временный объект. Не то же самое. Временные объекты были бы бесполезны, если бы их нельзя было инициализировать. - person user207421; 07.12.2011
comment
Обнимать? Как вы думаете, что означает std::string()=std::string("Hello World");? - person curiousguy; 07.12.2011
comment
@curiousguy Он объявляет временный объект с начальным значением. Он не «создает его, а затем назначает». Он также не принимает адрес временного объекта, о чем, собственно, и пойдет речь в этом потоке; это также не дает вам возможности изменить значение временного объекта, что я имел в виду под «доступом для записи». На самом деле это пример специальной оптимизации для инициализаторов, о которой я упоминал в своем ответе. - person user207421; 07.12.2011
comment
Он не создает его, а затем назначает. Конечно, он делает. x = y — это присваивание в C++. - person curiousguy; 07.12.2011
comment
@curiousguy Я повторю еще раз. Это объявление с инициализацией. Это не то же самое, что задание. Например, он не вызывает operator=. - person user207421; 07.12.2011
comment
Это объявление с инициализацией. Повторю еще раз: это присваивание. Это декларация Обнять??? декларирует что? Вы видимо не знаете С++. - person curiousguy; 07.12.2011
comment
давайте продолжим это обсуждение в чате - person curiousguy; 07.12.2011
comment
@curiousguy Нет, пока вы не ознакомились со стандартом C ++ и не поняли разницу между объявлением + инициализацией и назначением. - person user207421; 07.12.2011
comment
EJP, вы наконец ознакомились с грамматикой C++? - person curiousguy; 08.12.2011
comment
@curiousguy Да, я смотрю грамматики C++ уже 20 лет. Я отсылаю вас к ISO/IEC 14882 #8.5 «Инициализаторы». Если бы инициализация и присваивание были одним и тем же в C++, этот раздел не существовал бы. - person user207421; 13.12.2011
comment
@EJP: это не объявление, потому что у него нет идентификатора-декларатора, а список-деклараторов можно опустить только при объявлении класса или перечисления. - person Ben Voigt; 16.11.2016

Почему взятие адреса временного незаконно?

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

person Vishrant    schedule 05.02.2017