продление срока службы временных

Каково обоснование дизайна, позволяющее

const Foo& a = function_returning_Foo_by_value(); 

но не этот

 Foo& a = function_returning_Foo_by_value(); 

?

Что может пойти не так во второй строке (что в первой строке не пойдет не так)?

    Я отвечу на ваш вопрос … наоборот.

    Почему они разрешили Foo const& foo = fooByValue(); начать с ?

    Это облегчает жизнь (несколько), но вводит потенциальное неопределенное поведение повсюду.

     Foo const& fooByReference() { return fooByValue(); // error: returning a reference to a temporary } 

    Это, очевидно, неправильно, и действительно, компилятор послушно сообщит об этом. Согласно комментарию Томалака: он не санкционирован стандартом, но хорошие компиляторы должны сообщать об этом. Clang, gcc и MSVC. Я думаю, что Комо и ICC тоже.

     Foo const& fooByIndirectReference() { Foo const& foo = fooByValue(); // OK, you're allowed to bind a temporary return foo; // Generally accepted } 

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

    Я поднял ошибку на Клэнге, и Аргирису удалось диагностировать этот случай (на самом деле: p).

     Foo const& fooForwarder(Foo const&); // out of line implementation which forwards // the argument Foo const& fooByVeryIndirectReference() { return fooForwarder(fooByValue()); } 

    Временное созданное fooByValue привязано к времени жизни аргумента fooForwarder , которое покорно предоставляет копию (ссылки), копию, которая возвращается вызывающему, хотя она теперь указывает на эфир.

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

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

    Единственное решение, которое я могу понять (помимо WPA), – это решение времени исполнения: всякий раз, когда временный ограничивается ссылкой, вам нужно убедиться, что возвращаемая ссылка не имеет одного и того же адреса … и что тогда? assert ? вызывать исключение? И поскольку это всего лишь решение для выполнения, это явно неудовлетворительно.

    Идея привязки временного к ссылке является хрупкой.

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

    Есть много причин для этого, я просто покажу один classический пример с неявным расширением конверсий:

     struct Foo {}; bool CreateFoo( Foo*& result ) { result = new Foo(); return true; } struct SpecialFoo : Foo {}; SpecialFoo* p; if (CreateFoo(p)) { /* DUDE, WHERE'S MY OBJECT! */ } 

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

     bool validate_the_cat(const string&); string thing[3]; validate_the_cat(thing[1] + thing[2]); 

    Обратите внимание, что в этом случае не требовалось продление жизни.

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

    «Что может пойти не так» – это то, что вы изменяете объект, а затем мгновенно теряете изменения, и поэтому правило определяется таким образом, чтобы вы не допускали таких ошибок. Вы могли бы подумать, что если вы снова вызовете функцию, вы получите объект с вашими изменениями, что, конечно же, не будет, потому что вы изменили копию.

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

     std::string val; some_func_that_returns_a_string().swap( val ); 

    Иногда это может быть очень полезно.