Почему взятие адреса временно незаконно? - PullRequest
14 голосов
/ 29 ноября 2010

Я знаю, что код, написанный ниже, является недопустимым

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?

Ответы [ 7 ]

13 голосов
/ 29 ноября 2010
&std::string("Hello World")

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

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

42

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

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

6 голосов
/ 05 декабря 2011

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

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

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

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

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

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

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

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

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

4 голосов
/ 29 ноября 2010

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

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

99

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

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

$ 3.10 / 2 - «lvalue относится к объект или функция. Некоторое значение выражения - класс или cv-квалифицированный тип класса - также обратитесь к objects.47) "

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

Вот кое-что интересное:

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

int main(){
   f(42);
}

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

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

3 голосов
/ 29 ноября 2010

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

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

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

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

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

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

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

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

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

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

2 голосов
/ 29 ноября 2010

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

EDIT

Для downvoters:

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

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

EDIT2

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

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

0 голосов
/ 06 февраля 2017

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

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

0 голосов
/ 29 ноября 2010

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

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

...