views:

293

answers:

4

Given the following code:

#include <iostream>

struct implicit_t
{
    implicit_t(int x) :
        x_m(x)
    {
        std::cout << "ctor" << std::endl;
    }

    ~implicit_t()
    {
        std::cout << "dtor" << std::endl;
    }

    int x_m;
};

std::ostream& operator<<(std::ostream& s, const implicit_t& x)
{
    return s << x.x_m;
}

const implicit_t& f(const implicit_t& x)
{
    return x;
}

int main()
{
    std::cout << f(42) << std::endl;

    return 0;
}

I get the following output:

ctor
42
dtor

While I know this is correct, I'm not certain why. Is there anyone with stdc++ knowledge who can explain it to me?

A: 

f(42) constructs an unnamed implicit_t implicitly. It lives for the duration of it's containing scope, just as any auto variable would. Naturally, the d'tor gets called on return 0; of main().

Alex
If it lived for the duration of its containing scope, it would live until the end of main. It does not, and you can see this with another output inside main, after this statement, before the return.
Roger Pate
Oops, you are correct.
Alex
+6  A: 

Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. [12.2/3]

Roger Pate
"It's really that simple". In this case. Although just for laughs, 12.2/4 (which is *over the next page* in my edition of the standard) flatly contradicts this by saying, "There are two contexts in which temporaries are destroyed at a different point". Because C++ isn't *supposed* to be easy ;-)
Steve Jessop
Good point; however, in both /4 and /5, you are dealing with declaration statements instead of expression statements, and C++ treats those very differently (e.g. you can't have expression statements outside of functions), and I think most people's mental model differentiates them cleanly.
Roger Pate
+4  A: 

12.2 Temporary objects, clause 3: "Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created."

1.9 Program execution, clause 12: "A full-expression is an expression that is not a subexpression of another expression."

Dan
A: 

Since there is a constructor which can accept the argument passed into the F() function the complier creates the object on the fly before putting the arguments on the stack. As can be see in the disassembly below. literal numbers are treated by default as ints so there is an acceptable conversion.

001115C5  call        implicit_t::implicit_t (11112Ch) 
001115CA  mov         dword ptr [ebp-4],0 
001115D1  mov         esi,esp 
001115D3  mov         eax,dword ptr [__imp_std::endl (11A308h)] 
001115D8  push        eax  
001115D9  lea         ecx,[ebp-0D4h] 
001115DF  push        ecx  
001115E0  call        f (111113h)

Your temp object hangs around until the expression is fully evaluated. this can be made more evident if you add another call to your function.

int main()
{
    std::cout << f(42) << std::endl;

    std::cout <<f(80) << std::endl;

    return 0;
}

Which has an output of

ctor
42
dtor
ctor
80
dtor
rerun
Wrong regarding lifetime of temporary.
Pavel Minaev
In what way is that wrong I said the same thing as the accepted answer.
rerun
It's when you say the "life cycle of a temp object is only for the call". If by 'the call" you mean `f(42)` that's not correct. If it was you would see "42dtor" *before* the `endl`. The accepted answer points out that the temporary lasts until the end of the full *expression*.
quark
good point I will clarify in my post
rerun