Is this legal?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Michael Sparks

    Is this legal?

    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;
    }



  • Victor Bazarov

    #2
    Re: Is this legal?

    "Michael Sparks" <michael.sparks @remove.this.sb cglobal.net> wrote...[color=blue]
    > 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.[/color]
    Is[color=blue]
    > 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;
    > }[/color]

    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


    Comment

    • Claudio Jolowicz

      #3
      Re: Is this legal?

      On Sat, 17 Apr 2004, Michael Sparks wrote:
      [color=blue]
      >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;
      >}
      >[/color]

      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




      Comment

      • Michael Sparks

        #4
        Re: Is this legal?

        "Victor Bazarov" <v.Abazarov@com Acast.net> wrote in message
        news:L1igc.1602 25$K91.412208@a ttbi_s02...[color=blue]
        > Is this an interview or a homework question? Read about the lifetime of
        > a temporary if there is a constant reference bound to it.[/color]

        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


        Comment

        • Victor Bazarov

          #5
          Re: Is this legal?

          "Michael Sparks" <michael.sparks @remove.this.sb cglobal.net> wrote...[color=blue]
          > "Victor Bazarov" <v.Abazarov@com Acast.net> wrote in message
          > news:L1igc.1602 25$K91.412208@a ttbi_s02...[color=green]
          > > Is this an interview or a homework question? Read about the lifetime of
          > > a temporary if there is a constant reference bound to it.[/color]
          >
          > 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.[/color]

          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


          Comment

          • SaltPeter

            #6
            Re: Is this legal?


            "Michael Sparks" <michael.sparks @remove.this.sb cglobal.net> wrote in message
            news:YLhgc.8654 $3c6.3593@newss vr24.news.prodi gy.com...[color=blue]
            > 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.[/color]
            Is[color=blue]
            > it legal? If so, why?
            >[/color]

            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_insid e)
            {
            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.insid e_ = " << &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.




            Comment

            • Jorge Rivera

              #7
              Re: Is this legal?

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

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

              JLR

              Comment

              • Michael Sparks

                #8
                Re: Is this legal?

                "Claudio Jolowicz" <cj603@doc.ic.a c.uk> wrote in message
                news:Pine.LNX.4 .58.04041723152 10.2136@kiwi.do c.ic.ac.uk...[color=blue]
                > A temporary object is not destroyed if it is used to initialize a
                > reference. Example:[/color]

                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::outs ide_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


                Comment

                • Rolf Magnus

                  #9
                  Re: Is this legal?

                  Michael Sparks wrote:
                  [color=blue]
                  > "Claudio Jolowicz" <cj603@doc.ic.a c.uk> wrote in message
                  > news:Pine.LNX.4 .58.04041723152 10.2136@kiwi.do c.ic.ac.uk...[color=green]
                  >> A temporary object is not destroyed if it is used to initialize a
                  >> reference. Example:[/color]
                  >
                  > 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.[/color]

                  Which gets a reference as parameter, which is initialized with the
                  temporary.

                  Comment

                  • Jakob Bieling

                    #10
                    Re: Is this legal?

                    "Michael Sparks" <michael.sparks @remove.this.sb cglobal.net> wrote in message
                    news:B3jgc.8695 $wH6.8479@newss vr24.news.prodi gy.com...
                    [color=blue]
                    > In main(), the temporary is not used to initialize a reference.[/color]

                    It is!
                    [color=blue]
                    > It is
                    > simply passed as an argument to a constructor.[/color]

                    And that argument is of type 'inside_t const&,' meaning you bind the
                    temporary to a constant reference.
                    [color=blue]
                    > 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.[/color]

                    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)


                    Comment

                    • Jakob Bieling

                      #11
                      Re: Is this legal?

                      "Michael Sparks" <michael.sparks @remove.this.sb cglobal.net> wrote in message
                      news:S5jgc.8696 $9I6.5224@newss vr24.news.prodi gy.com...
                      [color=blue]
                      > 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.[/color]

                      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)


                      Comment

                      • Benoit Mathieu

                        #12
                        Re: Is this legal?

                        Michael Sparks wrote:[color=blue]
                        > 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?[/color]

                        [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_m e();
                        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

                        Comment

                        • Victor Bazarov

                          #13
                          Re: Is this legal?

                          "Benoit Mathieu" <benoit.mathieu 2@NOSPAM.free.f r.invalid> wrote...[color=blue]
                          > Michael Sparks wrote:[color=green]
                          > > 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.[/color][/color]
                          Is[color=blue][color=green]
                          > > it legal? If so, why?[/color]
                          >
                          > [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.
                          >[/color]

                          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


                          Comment

                          • Claudio Jolowicz

                            #14
                            Re: Is this legal?

                            On Sat, 17 Apr 2004, Michael Sparks wrote:
                            [color=blue]
                            >"Claudio Jolowicz" <cj603@doc.ic.a c.uk> wrote in message
                            >news:Pine.LNX. 4.58.0404172315 210.2136@kiwi.d oc.ic.ac.uk...[color=green]
                            >> A temporary object is not destroyed if it is used to initialize a
                            >> reference. Example:[/color]
                            >
                            >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.
                            >[/color]

                            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::insi de_t()" << endl; }
                            ~inside_t() { cout << "inside_t::~ins ide_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]
                            [color=blue]
                            >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.[/color]

                            Agree.
                            [color=blue]
                            >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.[/color]

                            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




                            Comment

                            • Benoit Mathieu

                              #15
                              Re: Is this legal?

                              Victor Bazarov wrote:[color=blue]
                              > So, binding a temporary _directly_ to a const reference is what causes
                              > the temporary to live as long as the reference[/color]

                              I'll check this in my Stroustrup tomorrow...
                              [color=blue]
                              > 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?[/color]

                              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.
                              [color=blue]
                              > 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;
                              > }[/color]

                              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.

                              Comment

                              Working...