lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Why does this crash?


From: Vadim Zeitlin
Subject: Re: [lmi] Why does this crash?
Date: Sat, 17 Apr 2010 16:43:33 +0200

On Sat, 17 Apr 2010 14:03:57 +0000 Greg Chicares <address@hidden> wrote:

GC> with the patch below (it's fine without). Shouldn't this principle:
GC>   
http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
GC> apply here?

 Hello,

 The principle applies only when you assign the value returned by the
function to a (const) reference. It doesn't apply if you use it in any
other way.

GC> I feel like I'm missing something obvious.
GC> 
GC> Index: xml_serializable.tpp
GC> ===================================================================
GC> --- xml_serializable.tpp    (revision 4841)
GC> +++ xml_serializable.tpp    (working copy)
GC> @@ -228,8 +228,8 @@
GC>      ,int                 file_version
GC>      )
GC>  {
GC> -    xml::node::const_iterator i = xml_lmi::retrieve_element(parent, name);
GC> -    std::string s = xml_lmi::get_content(*i);
GC> +    xml::element const& e = *xml_lmi::retrieve_element(parent, name);
GC> +    std::string s = xml_lmi::get_content(e);

 I.e. the only improvement I see here is that you could write

        xml::node::const_iterator const& i = xml_lmi::retrieve_element(parent, 
name);

to avoid making a copy of the iterator. You still need to name it to ensure
that its lifetime extends to the end of the block.


 FWIW here is the minimal (although still relatively long) example which
you can use for testing this:

        % cat return_ref_lifetime.cpp
        #include <stdio.h>

        struct S {
            S() { printf("S(%p) ctor\n", this); }
            S(const S& s) { printf("S(%p) copy ctor from %p\n", this, &s); }
            ~S() { printf("S(%p) dtor\n", this); }
        };

        struct Sptr {
            Sptr(const S& s) : s(s) {}
            ~Sptr() { puts("Sptr dtor"); }

            const S& operator*() const { return s; }

            S s;
        };

        Sptr GetSptr() { return Sptr(S()); }

        void Test1()
        {
            Sptr const& sptr = GetSptr();
            puts("Leaving Test1()");
        }

        void Test2()
        {
            S const& s = *GetSptr();
            puts("Leaving Test2()");
        }

        int main()
        {
            Test1();

            puts("");

            Test2();

            return 0;
        }
        % g++ -Wall return_ref_lifetime.cpp && ./a.out
        ...snip warnings about unused variables...
        S(0x7fff9c18f3ef) ctor
        S(0x7fff9c18f427) copy ctor from 0x7fff9c18f3ef
        S(0x7fff9c18f3ef) dtor
        Leaving Test1()
        Sptr dtor
        S(0x7fff9c18f427) dtor

        S(0x7fff9c18f40f) ctor
        S(0x7fff9c18f437) copy ctor from 0x7fff9c18f40f
        S(0x7fff9c18f40f) dtor
        Sptr dtor
        S(0x7fff9c18f437) dtor
        Leaving Test2()

You can clearly see that in the second test function Sptr object is
destroyed immediately. And this is the correct thing to do because there
are no references to this (anonymous) object and so no exceptions apply (I
probably could find verse and chapter if you're interested...).


 FWIW this is probably one of my favourite tricks in C++ just because it
allows ScopeGuard to work.

 Regards,
VZ

reply via email to

[Prev in Thread] Current Thread [Next in Thread]