2026-09-20
tracer_lifetime.cpp
#include <iostream>
struct Tracer {
int id;
explicit Tracer(int id) : id(id) { std::cout << "ctor " << id << "\n"; }
~Tracer() { std::cout << "dtor " << id << "\n"; }
};
void use(const Tracer& t) {
std::cout << "use " << t.id << "\n";
}
int main() {
std::cout << "start\n";
use(Tracer(1));
std::cout << "after call\n";
const Tracer& r = Tracer(2);
std::cout << "after bind\n";
}
Tracer(1)は関数useの引数として、Tracer(2)はローカルのconst参照rに直接束縛されています。これは何を出力するでしょうか?
Answer
start / ctor 1 / use 1 / dtor 1 / after call / ctor 2 / after bind / dtor 2 — 1行ずつ。use(Tracer(1))のTracer(1)は「関数呼び出しの引数」という一時オブジェクトで、それを含む式(use(...)の呼び出し全体)が終わった時点、つまりuseから戻った直後に破棄されます。だからdtor 1はuse 1のすぐあと、after callが出力されるより前に出ます。一方const Tracer& r = Tracer(2);は、一時オブジェクトをconst参照へ直接束縛する形で、この場合はC++の規則により一時オブジェクトの寿命がその参照(r)の寿命まで延長されます。rはmainのブロックスコープが終わるまで生きているローカル変数なので、Tracer(2)の破棄はafter bindが出力されたあと、mainを抜ける直前まで先延ばしになります。同じ「引数として一時オブジェクトを使う」ように見えても、関数の引数として渡すのか、参照に直接束縛するのかで寿命の終わり方がまったく違う、という点がこの例の要点です。