Ваш пример не выполняет вложенное продление жизни
В конструкторе
B(const A & a_) : a(a_) { std::cout << " B()" << std::endl; }
a_
здесь (переименован для экспозиции) не является временным. Является ли выражение временным, является синтаксическим свойством выражения, а id-выражение никогда не является временным. Таким образом, продление срока службы здесь не происходит.
Вот случай, когда может произойти продление жизни:
B() : a(A()) { std::cout << " B()" << std::endl; }
Однако, поскольку ссылка инициализируется в инициализаторе ctor, время жизни увеличивается только до конца функции. За [класс.время] р5 :
Временная привязка к элементу ссылки в конструкторе ctor-initializer (12.6.2) сохраняется до выхода из конструктора.
В вызове конструктору
B b((A())); //extra braces are needed!
Здесь мы привязываем ссылку к временному. [class.tevent] p5 говорит:
Временные связанный с опорным параметром в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов.
Поэтому временное A
уничтожается в конце оператора. Это происходит до того, как переменная B
будет уничтожена в конце блока, что объясняет ваши результаты регистрации.
В других случаях выполняется вложенное продление жизни
Агрегированная переменная инициализации
Совокупная инициализация структуры со ссылочным элементом может продлить срок жизни:
struct X {
const A &a;
};
X x = { A() };
В этом случае временное значение A
напрямую связано со ссылкой, и поэтому временное значение продлевается на время жизни до x.a
, что совпадает с временем жизни x
. (Предупреждение: до недавнего времени очень немногие компиляторы понимали это правильно).
Совокупная временная инициализация
В C ++ 11 вы можете использовать агрегатную инициализацию, чтобы инициализировать временную, и, таким образом, получить рекурсивное продление времени жизни:
struct A {
A() { std::cout << " A()" << std::endl; }
~A() { std::cout << "~A()" << std::endl; }
};
struct B {
const A &a;
~B() { std::cout << "~B()" << std::endl; }
};
int main() {
const B &b = B { A() };
std::cout << "-----" << std::endl;
}
С магистральным Clang или g ++ это дает следующий вывод:
A()
-----
~B()
~A()
Обратите внимание, что временные A
и временные B
продлены на весь срок службы. Поскольку строительство временного A
завершается первым, оно уничтожается последним.
В std::initializer_list<T>
инициализация
C ++ 11 std::initializer_list<T>
выполняет продление времени жизни, как будто путем привязки ссылки к базовому массиву. Поэтому мы можем выполнить вложенное время жизни, используя std::initializer_list
. Однако ошибки компилятора распространены в этой области:
struct C {
std::initializer_list<B> b;
~C() { std::cout << "~C()" << std::endl; }
};
int main() {
const C &c = C{ { { A() }, { A() } } };
std::cout << "-----" << std::endl;
}
Производит с хоботом Clang:
A()
A()
-----
~C()
~B()
~B()
~A()
~A()
и со стволом g ++:
A()
A()
~A()
~A()
-----
~C()
~B()
~B()
Они оба не правы; правильный вывод:
A()
A()
-----
~C()
~B()
~A()
~B()
~A()