Namaran

Code daily. Without assist.

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 1use 1のすぐあと、after callが出力されるより前に出ます。一方const Tracer& r = Tracer(2);は、一時オブジェクトをconst参照へ直接束縛する形で、この場合はC++の規則により一時オブジェクトの寿命がその参照(r)の寿命まで延長されます。rmainのブロックスコープが終わるまで生きているローカル変数なので、Tracer(2)の破棄はafter bindが出力されたあと、mainを抜ける直前まで先延ばしになります。同じ「引数として一時オブジェクトを使う」ように見えても、関数の引数として渡すのか、参照に直接束縛するのかで寿命の終わり方がまったく違う、という点がこの例の要点です。