Is this legal?

Discussion in 'C++' started by Michael Sparks, Apr 17, 2004.

  1. In this code, the temporary inside_t instantiated in main() goes out of
    scope by the time we hit the next line.
    MSVC 2003 doesn't complain about that even at the highest warning level. Is
    it legal? If so, why?

    struct inside_t
    {
    };

    struct outside_t
    {
    const inside_t& _inside;
    outside_t(const inside_t& inside) : _inside(inside)
    {
    }
    };

    int main()
    {
    const outside_t& outside=outside_t(inside_t());
    // now, outside._inside is referencing to nowhere
    return 0;
    }
    Michael Sparks, Apr 17, 2004
    #1
    1. Advertising

  2. "Michael Sparks" <> wrote...
    > In this code, the temporary inside_t instantiated in main() goes out of
    > scope by the time we hit the next line.
    > MSVC 2003 doesn't complain about that even at the highest warning level.

    Is
    > it legal? If so, why?
    >
    > struct inside_t
    > {
    > };
    >
    > struct outside_t
    > {
    > const inside_t& _inside;
    > outside_t(const inside_t& inside) : _inside(inside)
    > {
    > }
    > };
    >
    > int main()
    > {
    > const outside_t& outside=outside_t(inside_t());
    > // now, outside._inside is referencing to nowhere
    > return 0;
    > }


    Is this an interview or a homework question? Read about the lifetime of
    a temporary if there is a constant reference bound to it.

    Victor
    Victor Bazarov, Apr 17, 2004
    #2
    1. Advertising

  3. On Sat, 17 Apr 2004, Michael Sparks wrote:

    >In this code, the temporary inside_t instantiated in main() goes out of
    >scope by the time we hit the next line.
    >MSVC 2003 doesn't complain about that even at the highest warning level. Is
    >it legal? If so, why?
    >
    >struct inside_t
    >{
    >};
    >
    >struct outside_t
    >{
    > const inside_t& _inside;
    > outside_t(const inside_t& inside) : _inside(inside)
    > {
    > }
    >};
    >
    >int main()
    >{
    > const outside_t& outside=outside_t(inside_t());
    > // now, outside._inside is referencing to nowhere
    > return 0;
    >}
    >


    A temporary object is not destroyed if it is used to initialize a
    reference. Example:

    int main()
    {
    const int& r = int();
    std::cout << r << std::endl; //r still references the temporary object
    return 0;
    }

    --
    Claudio Jolowicz
    Claudio Jolowicz, Apr 17, 2004
    #3
  4. "Claudio Jolowicz" <> wrote in message
    news:p...
    > A temporary object is not destroyed if it is used to initialize a
    > reference. Example:


    Yes, I understand that. The question is about a more specific issue.

    In main(), the temporary is not used to initialize a reference. It is
    simply passed as an argument to a constructor.
    It is in the constructor where it is bound to the const reference, and at
    that point, it isn't known to be a temporary - it is just a parameter.

    Suppose I had broken the code into two compilation units:

    --------------------- first compilation unit ---------------------

    struct inside_t
    {
    };

    struct outside_t
    {
    const inside_t& _inside;
    outside_t(const inside_t& inside);
    };

    outside_t::eek:utside_t(const inside_t& inside) : _inside(inside)
    {
    }

    --------------------- end first compilation unit ---------------------

    --------------------- second compilation unit ---------------------

    struct inside_t
    {
    };

    struct outside_t
    {
    const inside_t& _inside;
    outside_t(const inside_t& inside);
    };

    int main()
    {
    const outside_t& outside=outside_t(inside_t());
    // now, outside._inside is referencing to nowhere
    return 0;
    }

    --------------------- end second compilation unit ---------------------

    Now, it is clear to see that when compiling main(), the compiler could not
    have any clue that the temporary inside_t is used to initialize a const
    reference. Therefore, it will destroy it after that line.

    Also, I have verified this behavior experimentally with MSVC. I more or
    less assumed that the behavior was correct, and that MSVC compiled it
    correctly - the "is this legal" question was more rhetorical than anything.

    What I'm really interested in is *why* this is legal.

    Mike
    Michael Sparks, Apr 18, 2004
    #4
  5. "Victor Bazarov" <> wrote in message
    news:L1igc.160225$K91.412208@attbi_s02...
    > Is this an interview or a homework question? Read about the lifetime of
    > a temporary if there is a constant reference bound to it.


    No, this is not a homework or interview question.
    Please see my reply to Claudio Jolowicz.

    Also, unless I'm mistaken it has little to do with the lifetime of a
    temporary if there is a const reference bound to it.
    If that is incorrect, please enlighten me.

    Mike
    Michael Sparks, Apr 18, 2004
    #5
  6. Michael Sparks

    SaltPeter Guest

    "Michael Sparks" <> wrote in message
    news:YLhgc.8654$...
    > In this code, the temporary inside_t instantiated in main() goes out of
    > scope by the time we hit the next line.
    > MSVC 2003 doesn't complain about that even at the highest warning level.

    Is
    > it legal? If so, why?
    >


    Its your responsability to guarentee an object's scope and lifetime, not the
    compiler's. In this case, the outside_t structure invokes a temporary
    inside_t object by *default* since outside_t's constructor *requires* a
    reference to some inside_t object.

    The compiler needs not satisfy the existance of the inside_t object after
    the outside_t's cstor was invoked.

    <snip>
    ____________________
    Lets define an inside_t object, pass it by reference in order to construct
    your outside_t object (which then initializes the inside_ member with a
    reference that actually refers to something real). Note that the inside_t
    object is never "part-of" the outside_t object, yet its now destroyed last.

    #include <iostream>

    struct inside_t
    {
    inside_t() { std::cout << "inside_t cstor\n"; }
    ~inside_t() { std::cout << "inside_t d~stor\n"; }
    };

    struct outside_t
    {
    outside_t(const inside_t& r_inside) : inside_(r_inside)
    {
    std::cout << "outside_t cstor\n";
    }
    ~outside_t() { std::cout << "outside_t d~stor\n"; }

    const inside_t& inside_;
    };

    int main()
    {
    inside_t in; // cstor invoked
    outside_t out(in); // cstor invoked, in passed by reference
    // display member inside_
    std::cout << "&out.inside_ = " << &out.inside_ << std::endl;

    const outside_t& ref_out = out;
    std::cout << "reference ref_out = " << &ref_out << std::endl;
    std::cout << "in's address is " << &in << std::endl;

    return 0;
    }
    _________
    Think of it this way: write a letter, put it in an envelope with some
    address specified and stick an appropriate postage stamp on it. Mail it.
    Nobody is going to build a home/apartment at destination in order to
    guarentee the delivery of that one piece of mail. A reference works exactly
    in the same way.
    SaltPeter, Apr 18, 2004
    #6
  7. "Michael Sparks" <> wrote...
    > "Victor Bazarov" <> wrote in message
    > news:L1igc.160225$K91.412208@attbi_s02...
    > > Is this an interview or a homework question? Read about the lifetime of
    > > a temporary if there is a constant reference bound to it.

    >
    > No, this is not a homework or interview question.
    > Please see my reply to Claudio Jolowicz.
    >
    > Also, unless I'm mistaken it has little to do with the lifetime of a
    > temporary if there is a const reference bound to it.
    > If that is incorrect, please enlighten me.


    If there are two const references in the program, and both are bound
    to the two temporaries in the program, then the lifetime of those two
    temporaries has _everything_ to do with it. I don't know how to
    enlighten you. Just take a look at the code.

    Victor
    Victor Bazarov, Apr 18, 2004
    #7
  8. Michael Sparks

    Jorge Rivera Guest


    >
    > int main()
    > {
    > const int& r = int();
    > std::cout << r << std::endl; //r still references the temporary object
    > return 0;
    > }
    >


    Try that code with you own class, and see if what you're saying holds
    true... (No destructor called)

    JLR
    Jorge Rivera, Apr 18, 2004
    #8
  9. Michael Sparks

    Rolf Magnus Guest

    Michael Sparks wrote:

    > "Claudio Jolowicz" <> wrote in message
    > news:p...
    >> A temporary object is not destroyed if it is used to initialize a
    >> reference. Example:

    >
    > Yes, I understand that. The question is about a more specific issue.
    >
    > In main(), the temporary is not used to initialize a reference. It is
    > simply passed as an argument to a constructor.


    Which gets a reference as parameter, which is initialized with the
    temporary.
    Rolf Magnus, Apr 18, 2004
    #9
  10. "Michael Sparks" <> wrote in message
    news:B3jgc.8695$...

    > In main(), the temporary is not used to initialize a reference.


    It is!

    > It is
    > simply passed as an argument to a constructor.


    And that argument is of type 'inside_t const&,' meaning you bind the
    temporary to a constant reference.

    > It is in the constructor where it is bound to the const reference, and at
    > that point, it isn't known to be a temporary - it is just a parameter.


    It is known as a reference to a constant 'inside_t.' Meaning, you are
    using a 'inside_t const&' to initialize another 'inside_t const&'.

    hth
    --
    jb

    (replace y with x if you want to reply by e-mail)
    Jakob Bieling, Apr 18, 2004
    #10
  11. "Michael Sparks" <> wrote in message
    news:S5jgc.8696$...

    > Also, unless I'm mistaken it has little to do with the lifetime of a
    > temporary if there is a const reference bound to it.
    > If that is incorrect, please enlighten me.


    Binding a temporary to a constant reference changes the lifetime of that
    temporary.

    hth
    --
    jb

    (replace y with x if you want to reply by e-mail)
    Jakob Bieling, Apr 18, 2004
    #11
  12. Michael Sparks wrote:
    > In this code, the temporary inside_t instantiated in main() goes out of
    > scope by the time we hit the next line.
    > MSVC 2003 doesn't complain about that even at the highest warning level. Is
    > it legal? If so, why?


    [code removed

    A simple test shows that this code does not work.
    See :
    - outside object is destroyed only at return 0, because it
    has been assigned to a reference. It is destroyed at the
    same time as the reference. This is ok.
    - inside object is destroyed before the outside objet that
    refers to it, this is a bug.

    // Test.cpp :

    #include <iostream>
    struct inside_t
    {
    inside_t() { std::cout << this << " inside_t constructed"
    << std::endl; }
    ~inside_t() { std::cout << this << " inside_t destroyed"
    << std::endl; }
    };
    struct outside_t
    {
    const inside_t& _inside;
    outside_t(const inside_t& inside) : _inside(inside)
    {
    std::cout << this << " outside_t constructed (refers to "
    << &_inside << ")" << std::endl;
    }
    ~outside_t() { std::cout << this << " ouside_t destroyed"
    << std::endl; }
    void print_me() const
    {
    std::cout << this << " outside still alive (refers to "
    << &_inside << ")" << std::endl;
    }
    };

    int main()
    {
    const outside_t& outside=outside_t(inside_t());
    std::cout << "before end" << std::endl;
    outside.print_me();
    return 0;
    }


    // Output:
    0xbffff940 inside_t constructed
    0xbffff950 outside_t constructed (refers to 0xbffff940)
    0xbffff940 inside_t destroyed
    before end
    0xbffff950 outside still alive (refers to 0xbffff940)
    0xbffff950 ouside_t destroyed
    Benoit Mathieu, Apr 18, 2004
    #12
  13. "Benoit Mathieu" <> wrote...
    > Michael Sparks wrote:
    > > In this code, the temporary inside_t instantiated in main() goes out of
    > > scope by the time we hit the next line.
    > > MSVC 2003 doesn't complain about that even at the highest warning level.

    Is
    > > it legal? If so, why?

    >
    > [code removed
    >
    > A simple test shows that this code does not work.
    > See :
    > - outside object is destroyed only at return 0, because it
    > has been assigned to a reference. It is destroyed at the
    > same time as the reference. This is ok.
    > - inside object is destroyed before the outside objet that
    > refers to it, this is a bug.
    >


    So, binding a temporary _directly_ to a const reference is what causes
    the temporary to live as long as the reference, however, preserving
    that const reference by initialising a different const reference with
    it is NOT OK, since the lifetime of the first reference could be shorter
    than that of the different const reference. Did I understand the gist
    of your example correctly?

    Here is a different illustration of the same problem:

    #include <iostream>
    using namespace std;

    struct A
    {
    char n;
    A(char n) : n(n) { cout << "A(" << n << ")\n"; }
    ~A() { cout << "~A(" << n << ")\n"; }
    };

    void foo(const A& ra)
    {
    static const A& a = ra; // preserve the reference
    static const A& aa = A('2'); // initialise another reference
    }

    int main()
    {
    foo(A('1'));
    cout << "before end\n";
    return 0;
    }

    I guess my reply to the OP was in error.

    Victor
    Victor Bazarov, Apr 18, 2004
    #13
  14. On Sat, 17 Apr 2004, Michael Sparks wrote:

    >"Claudio Jolowicz" <> wrote in message
    >news:p...
    >> A temporary object is not destroyed if it is used to initialize a
    >> reference. Example:

    >
    >Yes, I understand that. The question is about a more specific issue.
    >
    >In main(), the temporary is not used to initialize a reference. It is
    >simply passed as an argument to a constructor.
    >It is in the constructor where it is bound to the const reference, and at
    >that point, it isn't known to be a temporary - it is just a parameter.
    >


    In fact, the temporary is used to initialize a constant reference - the
    constructor's parameter. The lifetime of the temporary ends when the
    reference parameter goes out of scope, i.e. at the end of the
    constructor! This is most probably not what was intended, since the
    destructor will be called while the member reference is still bound to
    the temporary. Thanks for pointing this out.

    Here's yet another example to illustrate the problem (see also Victor's
    last post 2004-04-18 16:45:41 GMT):


    #include <iostream>
    using namespace std;

    struct inside_t
    {
    inside_t() { cout << "inside_t::inside_t()" << endl; }
    ~inside_t() { cout << "inside_t::~inside_t()" << endl; }
    };

    struct outside_t
    {
    outside_t(const inside_t& ri) : _ri(ri) {}
    private:
    const inside_t& _ri;
    };

    int main()
    {
    outside_t foo(inside_t());
    cout << "Shouldn't have destroyed temporary yet...?!" << endl;
    return 0;
    }


    [snipped your code example, which used two compilation units]

    >Now, it is clear to see that when compiling main(), the compiler could not
    >have any clue that the temporary inside_t is used to initialize a const
    >reference. Therefore, it will destroy it after that line.


    Agree.

    >Also, I have verified this behavior experimentally with MSVC. I more or
    >less assumed that the behavior was correct, and that MSVC compiled it
    >correctly - the "is this legal" question was more rhetorical than anything.
    >
    >What I'm really interested in is *why* this is legal.


    There's nothing wrong about passing a temporary as a reference
    parameter. Consider expression evaluation, where temporaries are passed
    by reference all the time.

    How can the compiler guess that the programmer really wanted to bind
    something else to the temporary using the parameter, and forgot it's the
    parameter's lifetime that counts, and not the lifetime of that something
    else?

    --
    Claudio Jolowicz
    Claudio Jolowicz, Apr 18, 2004
    #14
  15. "Benoit Mathieu" <> wrote...
    > Victor Bazarov wrote:
    > > So, binding a temporary _directly_ to a const reference is what causes
    > > the temporary to live as long as the reference

    >
    > I'll check this in my Stroustrup tomorrow...
    >
    > > however, preserving
    > > that const reference by initialising a different const reference with
    > > it is NOT OK, since the lifetime of the first reference could be shorter
    > > than that of the different const reference. Did I understand the gist
    > > of your example correctly?

    >
    > I would formulate the rule like this:
    > ******
    > When you build a reference with a temporary object (like
    > Object & o = Object(...); )
    > the lifetime of Object is the same as the lifetime of the
    > reference o.


    This is incorrect. You cannot bind a terporary to a non-const reference.
    This is not allowed by the Standard and compliant compilers should complain.

    > *******
    > Claudio Jolowicz has another phrase to express this rule, if
    > you prefer... (18 Apr 2004 21:38:38 +0000 (UTC))


    It has nothing to do with what I prefer. It's mandated by the Standard.

    > [...]
    Victor Bazarov, Apr 18, 2004
    #15
  16. Victor Bazarov wrote:
    > So, binding a temporary _directly_ to a const reference is what causes
    > the temporary to live as long as the reference


    I'll check this in my Stroustrup tomorrow...

    > however, preserving
    > that const reference by initialising a different const reference with
    > it is NOT OK, since the lifetime of the first reference could be shorter
    > than that of the different const reference. Did I understand the gist
    > of your example correctly?


    I would formulate the rule like this:
    ******
    When you build a reference with a temporary object (like
    Object & o = Object(...); )
    the lifetime of Object is the same as the lifetime of the
    reference o.
    *******
    Claudio Jolowicz has another phrase to express this rule, if
    you prefer... (18 Apr 2004 21:38:38 +0000 (UTC))

    This rule can be applied to the function call :
    // Declaration of function foo:
    foo(const Object & );

    foo(Object(...));

    Here we build a temporary Object(...), we build a reference
    with it, which is given to function foo. The lifetime of the
    function arguments is until the function's return. So the
    lifetime of the temporary Object(...) which was directely
    assigned to an argument is exactely the same.

    So this unique rule explains both why outside_t instance was
    not destroyed before "return 0" and why inside_t instance
    was destroyed immediately after outside_t(const inside_t &)
    constructor return in the OP's example.

    > Here is a different illustration of the same problem:
    >
    > #include <iostream>
    > using namespace std;
    >
    > struct A
    > {
    > char n;
    > A(char n) : n(n) { cout << "A(" << n << ")\n"; }
    > ~A() { cout << "~A(" << n << ")\n"; }
    > };
    >
    > void foo(const A& ra)
    > {
    > static const A& a = ra; // preserve the reference
    > static const A& aa = A('2'); // initialise another reference
    > }
    >
    > int main()
    > {
    > foo(A('1'));
    > cout << "before end\n";
    > return 0;
    > }


    You're right: this code is wrong.
    We apply the rule : Instance A('1') is destroyed when the
    reference it is assigned to is destroyed, which is when foo
    returns.
    Instance A('2') is destroyed when static const A& is
    destroyed, that is at the end of program execution.
    Benoit Mathieu, Apr 19, 2004
    #16
  17. >>Object & o = Object(...); )

    > This is incorrect. You cannot bind a terporary to a non-const reference.
    > This is not allowed by the Standard and compliant compilers should complain.


    Sorry, thanks for this rectification...

    Benoit
    Benoit Mathieu, Apr 19, 2004
    #17
  18. "Claudio Jolowicz" <> wrote in message
    news:p...
    > >What I'm really interested in is *why* this is legal.

    >
    > There's nothing wrong about passing a temporary as a reference
    > parameter. Consider expression evaluation, where temporaries are passed
    > by reference all the time.
    >
    > How can the compiler guess that the programmer really wanted to bind
    > something else to the temporary using the parameter, and forgot it's the
    > parameter's lifetime that counts, and not the lifetime of that something
    > else?


    I suppose I was just disappointed that the language allows this to happen,
    due to its design.

    It's apparent that the intent with references was to relieve some of the
    responsibility the programmer has when dealing with pointers.
    E.g., you don't have to check for NULL, and (usually) you don't have to
    worry about them pointing to invalid places.

    The language makes it pretty difficult in general to make a bad reference,
    outside of writing *NULL.

    Perhaps it was unavoidable for compatibility reasons, but this seems to me
    like a blemish on an otherwise valuable language feature. In other words,
    it's another thing to add to the long list of "things that might bite you in
    the ass".

    Note to anyone in this group with obsessive/compulsive disorder, and/or any
    ego malfunctions: I'm not saying I could have done any better. :)

    Mike
    Michael Sparks, Apr 19, 2004
    #18
    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. Valentin Tihomirov

    Is this legal?

    Valentin Tihomirov, Oct 21, 2003, in forum: VHDL
    Replies:
    20
    Views:
    1,216
    Jan Decaluwe
    Oct 29, 2003
  2. Divyang M
    Replies:
    9
    Views:
    603
    Divyang M
    May 18, 2005
  3. Divyang M
    Replies:
    1
    Views:
    552
    Jerzy Gbur
    May 15, 2005
  4. Weng Tianxiang
    Replies:
    12
    Views:
    1,374
  5. =?Utf-8?B?RGF2ZQ==?=

    Legal ViewState Characters

    =?Utf-8?B?RGF2ZQ==?=, May 24, 2004, in forum: ASP .Net
    Replies:
    1
    Views:
    352
    Patrice
    May 24, 2004
Loading...

Share This Page