Первое преимущество: если у вас нет файлов заголовков, вам придется включать исходные файлы в другие исходные файлы.
Второе преимущество: он позволяет обмениваться интерфейсами без совместного использования кода между разными единицами (разные разработчики, команды, компании и т. Д.) [ ! d1]
В стандарте рассматриваются два обстоятельства, при которых продлевается время жизни временного:
§12.2 / 4 Существует два контекста, в которых временные объекты уничтожаются в другой точке, чем конец fullexpression. Первый контекст - это когда выражение появляется как инициализатор для декларатора, определяющего объект. В этом контексте временное, которое содержит результат выражения, сохраняется до завершения инициализации объекта. [...]
§12.2 / 5 Второй контекст - это когда ссылка привязана к временному. [...]
blockquote>Ни один из этих двух не позволяет продлить время жизни временного путем более позднего связывания ссылки с другой ссылкой на константу. Но игнорируйте стандартное и думайте о том, что происходит:
Временные ряды создаются в стеке. Ну, технически, вызывающая конвенция может означать, что возвращаемое значение (временное), которое вписывается в регистры, может даже не создаваться в стеке, но нести меня. Когда вы привязываете постоянную ссылку на временный, компилятор семантически создает скрытую именованную переменную (поэтому конструктор копирования должен быть доступен, даже если он не вызывается) и связывает ссылку на эту переменную , Будет ли копия фактически сделана или удалена, является деталью: у нас есть неназванная локальная переменная и ссылка на нее.
Если стандарт разрешил ваш прецедент, то это будет означать, что время жизни временного объекта должно быть расширено до последней ссылки на эту переменную. Теперь рассмотрим это простое расширение вашего примера:
B * f () {B * bp = new B (A ()); return b; } void test () {B * p = f (); delete p; }
Теперь проблема заключается в том, что временная (давайте называть ее
_T
) связана вf ()
, она ведет себя как a там есть локальная переменная. Ссылка связана внутри* bp
. Теперь время жизни объекта выходит за пределы функции, создавшей временную, но поскольку_T
не было динамически распределено, это невозможно.Вы можете попытаться обосновать усилия, которые потребуются для продлить время жизни временного в этом примере, и ответ заключается в том, что он не может быть выполнен без какой-либо формы GC.
§12.2 / 5 говорит
Временная привязка к эталонному параметру в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов.
blockquote>Довольно вырезано и высушено.
Нет, расширенное время жизни больше не расширяется, передавая ссылку.
Во втором случае временное связано с параметром a и уничтожается на конец жизненного цикла параметра - конец конструктора.
В стандарте явно указано:
Временная привязка к ссылочному элементу в ctor-инициализаторе конструктора (12.6. 2) сохраняется, пока конструктор не выйдет.
blockquote>
§12.2 / 5 говорит & ldquo; Второй контекст [когда время жизни временного расширено] - это когда ссылка привязана к временному. & rdquo; Взято буквально, это ясно говорит о том, что срок жизни должен быть расширен в вашем случае; ваш B :: a
, безусловно, связан с временным. (Ссылка привязывается к объекту, и я не вижу другого объекта, к которому он мог бы привязываться). Однако это очень плохая формулировка; Я уверен, что подразумевается & ldquo; Второй контекст - это когда используется временное значение для инициализации ссылки, & rdquo; , а расширенное время жизни соответствует значению ссылки, инициализированной выражением rvalue временное, а не любое из других ссылок, которые впоследствии могут быть связаны с объектом. В соответствии с этим формулировка требует чего-то, что просто не реализуемо: рассмотрите:
void f (A const & amp; a) {static A const & amp; localA = a; }
с:
f (A ());
Где должен компилятор поставить A ()
(учитывая, что он вообще не может видеть код f ()
и doesn '
Я думаю, что это стоит DR.
Могу добавить, что есть текст, который настоятельно предлагает, чтобы мой интерпретация намерения верна. Представьте, что у вас был второй конструктор для B
:
B :: B (): a (A ()) {}
В этом случае B :: a
будет непосредственно инициализироваться временным; время жизни этого временного должно быть расширено даже по моей интерпретации. Тем не менее, стандарт делает конкретное исключение для этого случая; такое временное действие сохраняется только до тех пор, пока конструктор не выйдет (что снова оставит вас с обвисшей ссылкой). Это исключение дает очень сильное указание на то, что авторы стандарта не намеревались ссылаться на ссылки на члены в классе, чтобы продлить время жизни любых временных связей, к которым они привязаны; опять же, мотивация - это реализуемость. Представьте себе, что вместо
B b ((A ()));
вы написали:
B * b = new B (A ());
Где должен компилятор помещать временный A ()
так, чтобы это было время жизни для динамически выделенного B
?
B :: a
привязан к временному. Выражение, которое оно связано, формируется (неявным) разыменованием параметра. Это l-значение (хотя const
), а не временное, в этом контексте. Текст C ++ 0x также очень ясен об этих случаях: «Временная привязка к эталонному параметру в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов. & Quot; и "Временная привязка к ссылке в новом инициализаторе (5.3.4) сохраняется до завершения полного выражения, содержащего новый инициализатор. & quot;
– Ben Voigt
8 August 2011 в 00:29
Я не знаю о стандартах, но могу обсудить некоторые факты, которые я видел в нескольких предыдущих вопросах.
Первый вывод аналогичен очевидным причинам, когда a
и b
находятся в одном и том же объеме. Также a
разрушается после b
, потому что он построен до b
.
Предполагаю, что вас больше интересует второй вывод. Прежде чем начать, следует отметить, что следующие виды создания объектов (автономные временные):
{A (); }
продлится до следующего ;
, а не для окружающего его блока. [D0] Demo . Во втором случае, когда вы это сделаете,
B b ((A ()));
, таким образом A ()
уничтожается, как только заканчивается создание объекта B ()
. Поскольку ссылка const может быть привязана к временному, это не даст ошибку компиляции. Однако это приведет к логической ошибке, если вы попытаетесь получить доступ к B :: a
, который теперь уже связан с переменной области видимости.
В вашем первом прогоне объекты уничтожаются в том порядке, в котором они были нажаты в стеке -> то есть нажмите A, нажмите B, поп B, поп A.
Во втором запуске A срок службы заканчивается конструкцией b. Поэтому он создает A, он создает B от A, время жизни A заканчивается, поэтому оно уничтожается, а затем B уничтожается. Имеет смысл ...
В вашем примере не выполняется вложенное расширение жизни
В конструкторе
B (const A & amp; a_): a ( a_) {std :: cout & lt; "B ()" & lt; станд :: епсИ; }
Здесь a _
(переименованный для изложения) не является временным. Является ли выражение временным синтаксическим свойством выражения, а id-выражение никогда не является временным. Таким образом, здесь не существует расширения продолжительности жизни.
Вот случай, когда произойдет расширение продолжительности жизни:
B (): a (A ()) {std :: cout & lt ; & Lt; "B ()" & lt; станд :: епсИ; }
Однако, поскольку ссылка инициализируется в ctor-инициализаторе, время жизни продолжается только до конца функции. Per [class.temporary] p5:
Временная привязка к ссылочному элементу в конструкторе ctor-initializer (12.6.2) сохраняется до выхода конструктора. [ ! d35] blockquote>
В вызове конструктора
B b ((A ())); // нужны дополнительные скобки!
Здесь являются , связывающими ссылку на временную. [class.temporary] p5 говорит:
Временная привязка к ссылочному параметру в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов.
blockquote>Поэтому временный файл
A
уничтожается в конце инструкции. Это происходит до того, как переменнаяB
будет уничтожена в конце блока, объясняя ваш выход в журнал.Другие случаи выполняют вложенное расширение продолжительности жизни
Агрегатная инициализация переменных
Агрегатная инициализация структуры с помощью элемента-ссылки может продлить жизнь:
struct X {const A & amp; a; }; X x = {A ()};
В этом случае временное время
A
привязано непосредственно к ссылке, поэтому временное продление на всю жизнь доxa
, что совпадает с временем жизниx
.Агрегировать временную инициализацию
В C ++ 11 вы можете использовать агрегатную инициализацию для инициализировать временное и, следовательно, получить рекурсивное расширение продолжительности жизни:
struct A {A () {std :: cout & lt; "A ()" & lt; станд :: епсИ; } ~ A () {std :: cout & lt; "~ A ()" & lt; станд :: епсИ; }}; struct B {const A & amp; a; ~ B () {std :: cout & lt; "~ B ()" & lt; станд :: епсИ; }}; int main () {const B & amp; b = B {A ()}; std :: cout & lt; "-----" & lt; станд :: епсИ; }
С помощью соединительной линии Clang или g ++ это производит следующий вывод:
A () ----- ~ B () ~ A ()
Обратите внимание, что временные расширения
A
временно иB
временно продлены. Поскольку сначала выполняется временная постройкаA
, она уничтожается последним.В
std :: initializer_list & lt; T & gt;
инициализацияC ++ 11
std :: initializer_list & lt; T & gt;
выполняет расширение продолжительности жизни, как если бы он привязывал ссылку к базовому массиву. Поэтому мы можем выполнить вложенное расширение продолжительности жизни, используяstd :: initializer_list
. Однако ошибки в компиляторе распространены в этой области:struct C {std :: initializer_list & lt; B & gt; б; ~ C () {std :: cout & lt; "~ C ()" & lt; станд :: епсИ; }}; int main () {const C & amp; c = C {{{A ()}, {A ()}}}; std :: cout & lt; "-----" & lt; станд :: епсИ; }
Производится с стволом Clang:
A () A () ----- ~ C () ~ B () ~ B () ~ A () ~ A ()
и с g ++ trunk:
A () A () ~ A () ~ A () - --- ~ C () ~ B () ~ B ()
Это оба неправильно; правильный выход:
A () A () ----- ~ C () ~ B () ~ A () ~ B () ~ A ()