Temporary Object Lifetime

Discussion in 'C++' started by better_cs_now@yahoo.com, Jun 12, 2008.

  1. Guest

    Hello all,

    class Foo {/* Details don't matter */};

    class Bar {
    public:
    Bar(): m_Foo(/* Construct a Foo however it wants to be constructed
    */);
    const Foo &GetFoo() const { return m_Foo; }
    private:
    Foo m_Foo;
    };

    int main() {
    const Foo &OneFoo = Bar().GetFoo();
    // Do stuff with OneFoo. Is this legal given the use of
    // temporaries above?
    }

    Thanks,
    Dave
    , Jun 12, 2008
    #1
    1. Advertising

  2. On Jun 12, 5:17 pm, ""
    <> wrote:
    > int main() {
    > const Foo &OneFoo = Bar().GetFoo();
    > // Do stuff with OneFoo. Is this legal given the use of
    > // temporaries above?


    Not it is not.
    Check this out and give your self an answer
    #include <iostream>
    #include <cstdlib>

    class Foo {
    public:
    ~Foo(){std::cerr << "die foo" << std::endl;}
    };

    class Bar {
    public:
    Bar(): m_Foo(){}
    ~Bar(){std::cerr << "die bar" << std::endl;}
    const Foo &GetFoo() const { return m_Foo; }
    private:
    Foo m_Foo;

    };

    int main() {
    const Foo &OneFoo = Bar().GetFoo();
    std::cerr << "do stuff with OneFoo?" << std::endl;


    }
    Darío Griffo, Jun 12, 2008
    #2
    1. Advertising

  3. James Kanze Guest

    On Jun 12, 10:17 pm, ""
    <> wrote:

    > class Foo {/* Details don't matter */};


    > class Bar {
    > public:
    > Bar(): m_Foo(/* Construct a Foo however it wants to be constructed
    > */);
    > const Foo &GetFoo() const { return m_Foo; }
    > private:
    > Foo m_Foo;
    > };


    > int main() {
    > const Foo &OneFoo = Bar().GetFoo();
    > // Do stuff with OneFoo. Is this legal given the use of
    > // temporaries above?
    > }


    No. The lifetime of the temporary is until the end of the full
    expression. The lifetime of a temporary will be extended if it
    is used to initialize a const reference, but that's not the case
    anywhere in your code above. (The reference in the return value
    is initialized with an lvalue, not a temporary, and the
    reference in main is initialized with another reference.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jun 13, 2008
    #3
  4. James Kanze Guest

    On Jun 12, 10:40 pm, Victor Bazarov <> wrote:
    > wrote:
    > > class Foo {/* Details don't matter */};


    > > class Bar {
    > > public:
    > > Bar(): m_Foo(/* Construct a Foo however it wants to be constructed
    > > */);
    > > const Foo &GetFoo() const { return m_Foo; }
    > > private:
    > > Foo m_Foo;
    > > };


    > > int main() {
    > > const Foo &OneFoo = Bar().GetFoo();
    > > // Do stuff with OneFoo. Is this legal given the use of
    > > // temporaries above?
    > > }


    > I don't believe so. The 'Bar' temporary is not bound to the
    > reference, so it's not going to live beyond 'OneFoo's
    > initialisation.


    That's irrelevant. The fact that a reference is bound to a
    temporary has no effect on the lifetime of the temporary. The
    only time the lifetime of the temporary is extended is when the
    temporary is used to initialize the reference, and that's not
    the case here: the reference in main is initialized with another
    reference, and the reference returned by Bar::GetFoo() is
    initialized by an lvalue, not a temporary (and reference return
    values are a special case anyway, and don't extend the lifetime
    of the temporary, even if they're initialized with one).

    > Had 'Bar' derived from 'Foo' *and* you did


    > const Foo &OneFoo = Bar();


    > , then the 'Bar' would have a subobject of type 'Foo' that ought to
    > survive longer than the full expression, and as the superobject to the
    > temporary bound to a reference, the 'Bar' temporary would have to stay
    > alive as long as 'OneFoo'.


    > But your case is not that. In your case you create a
    > reference to a data member (which does live even shorter than
    > the 'Bar' object that contains it), so as soon as the
    > initialisation is complete, the 'OneFoo' reference becomes
    > *invalid*.


    The key isn't whether something is a subobject or not. In his
    initial example, the reference is bound to a sub-object of the
    temporary as well. The key here is that the temporary is
    initializing the reference directly; if m_Foo were public and
    you wrote:

    Foo const& &OneFoo = Bar().m_Foo ;

    it would be fine as well. It's the indirections in his case
    which break the code.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jun 13, 2008
    #4
  5. Ucayaly Fish Guest

    Hi,

    > That's irrelevant. The fact that a reference is bound to a
    > temporary has no effect on the lifetime of the temporary. The
    > only time the lifetime of the temporary is extended is when the
    > temporary is used to initialize the reference,


    Is it a compiler deficiency or regular standard complience if
    additional copy constructor is called during such an initialization?

    Slightly modified sample with string member and a pointer:

    ===code tmptst1.cpp ===

    #include <iostream>
    #include <cstdlib>
    #include <string>

    class Foo {
    public:
    Foo( const std::string& str ) : msg(str), p(0)
    {std::cerr << "up foo" << std::endl;
    p = new char[10];}
    Foo( const Foo& src ) : msg( "bad foo" ), p(0)
    {std::cerr << "copy foo" << std::endl;
    p = new char[20];}
    ~Foo()
    {std::cerr << "die foo" << std::endl;
    delete[] p;}
    void doSomeThing() const { std::cout << msg << std::endl; }
    private:
    std::string msg;
    char* p;

    };


    class Bar {
    public:
    Bar( const std::string& str ): m_Foo( str )
    {std::cerr << "up bar" << std::endl;}
    ~Bar(){std::cerr << "die bar" << std::endl;}
    const Foo &GetFoo() const { return m_Foo; }

    Foo m_Foo;


    };


    int main() {
    // const Foo &OneFoo = Bar( "Hi there!" ).GetFoo();
    const Foo &OneFoo = Bar( "Hi there!" ).m_Foo;
    std::cerr << "do stuff with OneFoo?" << std::endl;
    OneFoo.doSomeThing();

    }
    ===code tmptst1.cpp ===
    produces this output if compiled with MSVC.NET 2008

    cl /EHsc /O2 /NDEBUG tmptst1.cpp

    up foo
    up bar
    copy foo
    die bar
    die foo
    do stuff with OneFoo?
    bad foo
    die foo
    Ucayaly Fish, Jun 13, 2008
    #5
  6. James Kanze Guest

    On Jun 13, 12:34 pm, Ucayaly Fish <> wrote:

    > > That's irrelevant. The fact that a reference is bound to a
    > > temporary has no effect on the lifetime of the temporary. The
    > > only time the lifetime of the temporary is extended is when the
    > > temporary is used to initialize the reference,


    > Is it a compiler deficiency or regular standard complience if
    > additional copy constructor is called during such an initialization?


    Yes:).

    Seriously, it depends on what version of the standard you're
    looking at. All of the currently approved versions *require* a
    copy (and then authorize it to be optimized away).

    > Slightly modified sample with string member and a pointer:


    > ===code tmptst1.cpp ===
    >
    > #include <iostream>
    > #include <cstdlib>
    > #include <string>
    >
    > class Foo {
    > public:
    > Foo( const std::string& str ) : msg(str), p(0)
    > {std::cerr << "up foo" << std::endl;
    > p = new char[10];}
    > Foo( const Foo& src ) : msg( "bad foo" ), p(0)
    > {std::cerr << "copy foo" << std::endl;
    > p = new char[20];}
    > ~Foo()
    > {std::cerr << "die foo" << std::endl;
    > delete[] p;}
    > void doSomeThing() const { std::cout << msg << std::endl; }
    > private:
    > std::string msg;
    > char* p;
    > };
    >
    > class Bar {
    > public:
    > Bar( const std::string& str ): m_Foo( str )
    > {std::cerr << "up bar" << std::endl;}
    > ~Bar(){std::cerr << "die bar" << std::endl;}
    > const Foo &GetFoo() const { return m_Foo; }


    > Foo m_Foo;
    > };
    >
    > int main() {
    > // const Foo &OneFoo = Bar( "Hi there!" ).GetFoo();
    > const Foo &OneFoo = Bar( "Hi there!" ).m_Foo;
    > std::cerr << "do stuff with OneFoo?" << std::endl;
    > OneFoo.doSomeThing();
    > }


    The standard isn't really very clear here. My interpretation
    was that it more or less required the Bar object to be
    destructed at the end of the full expression, while requiring
    that the Foo object have its lifetime extended (and that this
    was the motivation behind "allowing" the copy). This section
    has been reworked some in the current draft, and FROM MEMORY, I
    think the draft forbids the copy, and requires extension of the
    Bar object's (the containing object's) lifetime. We're in the
    middle of moving here, however, and my copies of the various
    versions of the standard are on a network which is not reachable
    for the moment, so I can't verify this one way or the other.

    > ===code tmptst1.cpp ===
    > produces this output if compiled with MSVC.NET 2008


    > cl /EHsc /O2 /NDEBUG tmptst1.cpp


    > up foo
    > up bar
    > copy foo
    > die bar
    > die foo
    > do stuff with OneFoo?
    > bad foo
    > die foo


    It's definitely conform with C++03, and from memory, it's also
    what g++ does (but since NIS isn't working, I can only access
    local disks on my Unix machines, so I can't try anything).

    Arguably, the compiler is required to call Bar's destructor at
    the end of the full expression, and if it does, the only way to
    extend the lifetime of the temporary is to make a copy.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orient�e objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place S�mard, 78210 St.-Cyr-l'�cole, France, +33 (0)1 30 23 00 34
    James Kanze, Jun 13, 2008
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. pt
    Replies:
    8
    Views:
    762
  2. Replies:
    7
    Views:
    3,184
    James Kanze
    Feb 12, 2008
  3. Juha Nieminen
    Replies:
    2
    Views:
    266
  4. Igor R.
    Replies:
    2
    Views:
    413
    James Kanze
    Jan 28, 2009
  5. Daniel
    Replies:
    2
    Views:
    198
    Daniel
    Jun 11, 2013
Loading...

Share This Page