Returned object assigned to reference

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Andrew Ward

    Returned object assigned to reference

    Consider the following:

    #include <iostream>

    struct X
    {
    int x;
    X() { x = 6; }
    };

    X val()
    {
    return X();
    }

    int main()
    {
    X & x = val();
    X x2 = x;
    std::cout << x.x << " " << x2.x;
    }

    What is the lifetime of the object returned by the call to val()? I
    would have thought it should be destroyed immediately, creating a
    dangling reference, yet the program works, does the returned object
    live until the end of it's enclosing scope?

  • Alf P. Steinbach

    #2
    Re: Returned object assigned to reference

    * Andrew Ward:[color=blue]
    > Consider the following:
    >
    > #include <iostream>
    >
    > struct X
    > {
    > int x;
    > X() { x = 6; }
    > };
    >
    > X val()
    > {
    > return X();
    > }
    >
    > int main()
    > {
    > X & x = val();
    > X x2 = x;
    > std::cout << x.x << " " << x2.x;
    > }
    >
    > What is the lifetime of the object returned by the call to val()? I
    > would have thought it should be destroyed immediately, creating a
    > dangling reference, yet the program works, does the returned object
    > live until the end of it's enclosing scope?[/color]

    Anything, because this is Undefined Behavior.

    A reference to const would be another matter.

    Then the reference would be valid until the end of the scope.


    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?

    Comment

    • Andrew Ward

      #3
      Re: Returned object assigned to reference

      Could you please explain what you mean by "A reference to const would
      be another matter." by modifying the example given.

      Comment

      • Alf P. Steinbach

        #4
        Re: Returned object assigned to reference

        * Andrew Ward:[color=blue]
        > Could you please explain what you mean by "A reference to const would
        > be another matter." by modifying the example given.[/color]

        Please quote what you're replying to; I don't know what the heck you're
        talking about.

        --
        A: Because it messes up the order in which people normally read text.
        Q: Why is it such a bad thing?
        A: Top-posting.
        Q: What is the most annoying thing on usenet and in e-mail?

        Comment

        • Andrew Ward

          #5
          Re: Returned object assigned to reference

          Alf P. Steinbach wrote:[color=blue]
          > * Andrew Ward:[color=green]
          > > Could you please explain what you mean by "A reference to const would
          > > be another matter." by modifying the example given.[/color]
          >
          > Please quote what you're replying to; I don't know what the heck you're
          > talking about.
          >[/color]

          My ISP has just recently decided to stop providing a usenet server, so
          as I have not found another server to use yet I posted that message
          from google groups, and assumed it would automatically quote you,
          apparently not.

          Comment

          • Old Wolf

            #6
            Re: Returned object assigned to reference

            Alf P. Steinbach wrote:[color=blue]
            > * Andrew Ward:[color=green]
            >> X val() { return X(); }
            >>
            >> int main()
            >> {
            >> X & x = val();
            >>
            >> What is the lifetime of the object returned by the call to val()? I
            >> would have thought it should be destroyed immediately, creating a
            >> dangling reference, yet the program works, does the returned object
            >> live until the end of it's enclosing scope?[/color]
            >
            > Anything, because this is Undefined Behavior.[/color]

            Is a diagnostic required?
            [color=blue]
            > A reference to const would be another matter.
            > Then the reference would be valid until the end of the scope.[/color]

            To answer the question asked by the OP in a poorly-written
            reply:

            X const &x = val();

            would not lead to undefined behaviour, and the lifetime of
            the temporary object is extended to match the lifetime
            of the reference.

            Comment

            • Alf P. Steinbach

              #7
              Re: Returned object assigned to reference

              * Old Wolf:[color=blue]
              > Alf P. Steinbach wrote:[color=green]
              >> * Andrew Ward:[color=darkred]
              >>> X val() { return X(); }
              >>>
              >>> int main()
              >>> {
              >>> X & x = val();
              >>>
              >>> What is the lifetime of the object returned by the call to val()? I
              >>> would have thought it should be destroyed immediately, creating a
              >>> dangling reference, yet the program works, does the returned object
              >>> live until the end of it's enclosing scope?[/color]
              >> Anything, because this is Undefined Behavior.[/color]
              >
              > Is a diagnostic required?[/color]

              Undefined Behavior does not require a diagnostic: it's Undefined what
              happens... ;-)

              --
              A: Because it messes up the order in which people normally read text.
              Q: Why is it such a bad thing?
              A: Top-posting.
              Q: What is the most annoying thing on usenet and in e-mail?

              Comment

              • Alf P. Steinbach

                #8
                Re: Returned object assigned to reference

                * Alf P. Steinbach:[color=blue]
                > * Old Wolf:[color=green]
                >> Alf P. Steinbach wrote:[color=darkred]
                >>> * Andrew Ward:
                >>>> X val() { return X(); }
                >>>>
                >>>> int main()
                >>>> {
                >>>> X & x = val();
                >>>>
                >>>> What is the lifetime of the object returned by the call to val()? I
                >>>> would have thought it should be destroyed immediately, creating a
                >>>> dangling reference, yet the program works, does the returned object
                >>>> live until the end of it's enclosing scope?
                >>> Anything, because this is Undefined Behavior.[/color]
                >>
                >> Is a diagnostic required?[/color]
                >
                > Undefined Behavior does not require a diagnostic: it's Undefined what
                > happens... ;-)[/color]

                Hey, wait, that's true, but the function doesn't return a reference.

                So it's not UB, it's simply something that should not compile (i.e. a
                diagnostic is required).

                I thought this was something that compiled.

                --
                A: Because it messes up the order in which people normally read text.
                Q: Why is it such a bad thing?
                A: Top-posting.
                Q: What is the most annoying thing on usenet and in e-mail?

                Comment

                • Andrew Ward

                  #9
                  Re: Returned object assigned to reference

                  Alf P. Steinbach wrote:[color=blue]
                  > * Alf P. Steinbach:[color=green]
                  > > * Old Wolf:[color=darkred]
                  > >> Alf P. Steinbach wrote:
                  > >>> * Andrew Ward:
                  > >>>> X val() { return X(); }
                  > >>>>
                  > >>>> int main()
                  > >>>> {
                  > >>>> X & x = val();
                  > >>>>
                  > >>>> What is the lifetime of the object returned by the call to val()? I
                  > >>>> would have thought it should be destroyed immediately, creating a
                  > >>>> dangling reference, yet the program works, does the returned object
                  > >>>> live until the end of it's enclosing scope?
                  > >>> Anything, because this is Undefined Behavior.
                  > >>
                  > >> Is a diagnostic required?[/color]
                  > >
                  > > Undefined Behavior does not require a diagnostic: it's Undefined what
                  > > happens... ;-)[/color]
                  >
                  > Hey, wait, that's true, but the function doesn't return a reference.
                  >
                  > So it's not UB, it's simply something that should not compile (i.e. a
                  > diagnostic is required).
                  >
                  > I thought this was something that compiled.
                  >[/color]

                  Thanks both of you for your replies. If I understand correctly it would
                  be a compiler bug if the original posted code compiled without error.
                  This is the case with VS 2003 and 2005. But GCC 3.3.5 reports the error
                  correctly with this message:
                  error: invalid initialization of non-const reference of type 'X&' from
                  temporary of type 'X'

                  Comment

                  • rageratwork@gmail.com

                    #10
                    Re: Returned object assigned to reference

                    Alf P. Steinbach wrote:[color=blue]
                    > * Alf P. Steinbach:[color=green]
                    > > * Old Wolf:[color=darkred]
                    > >> Alf P. Steinbach wrote:
                    > >>> * Andrew Ward:
                    > >>>> X val() { return X(); }
                    > >>>>
                    > >>>> int main()
                    > >>>> {
                    > >>>> X & x = val();
                    > >>>>
                    > >>>> What is the lifetime of the object returned by the call to val()? I
                    > >>>> would have thought it should be destroyed immediately, creating a
                    > >>>> dangling reference, yet the program works, does the returned object
                    > >>>> live until the end of it's enclosing scope?
                    > >>> Anything, because this is Undefined Behavior.
                    > >>
                    > >> Is a diagnostic required?[/color]
                    > >
                    > > Undefined Behavior does not require a diagnostic: it's Undefined what
                    > > happens... ;-)[/color]
                    >
                    > Hey, wait, that's true, but the function doesn't return a reference.
                    >
                    > So it's not UB, it's simply something that should not compile (i.e. a
                    > diagnostic is required).
                    >
                    > I thought this was something that compiled.[/color]

                    I thought this was legal and a temporary copy of X was returned from
                    val() to satisfy the rhs of operator=(). Is this not so?

                    Dave.

                    Comment

                    • Jonathan Mcdougall

                      #11
                      Re: Returned object assigned to reference

                      rageratwork@gma il.com wrote:[color=blue]
                      > Alf P. Steinbach wrote:[color=green]
                      > > * Alf P. Steinbach:[color=darkred]
                      > > > * Old Wolf:
                      > > >> Alf P. Steinbach wrote:
                      > > >>> * Andrew Ward:
                      > > >>>> X val() { return X(); }
                      > > >>>>
                      > > >>>> int main()
                      > > >>>> {
                      > > >>>> X & x = val();
                      > > >>>>
                      > > >>>> What is the lifetime of the object returned by the call to val()? I
                      > > >>>> would have thought it should be destroyed immediately, creating a
                      > > >>>> dangling reference, yet the program works, does the returned object
                      > > >>>> live until the end of it's enclosing scope?
                      > > >>> Anything, because this is Undefined Behavior.
                      > > >>
                      > > >> Is a diagnostic required?
                      > > >
                      > > > Undefined Behavior does not require a diagnostic: it's Undefined what
                      > > > happens... ;-)[/color]
                      > >
                      > > Hey, wait, that's true, but the function doesn't return a reference.
                      > >
                      > > So it's not UB, it's simply something that should not compile (i.e. a
                      > > diagnostic is required).
                      > >
                      > > I thought this was something that compiled.[/color]
                      >
                      > I thought this was legal and a temporary copy of X was returned from
                      > val() to satisfy the rhs of operator=(). Is this not so?[/color]

                      No. val() returns an rvalue and it is bound to a non-const reference,
                      which is illegal. With

                      X& val() // <-- reference
                      {
                      return X(); // <-- rvalue
                      }

                      int main()
                      {
                      X& x = val(); // <-- ok
                      }

                      the assignment would compile (though having undefined behavior), but
                      then the return statement would not compile (X() is an rvalue, so the
                      same problem arises). Finally,

                      X& val() // <-- reference
                      {
                      X x;
                      return x; // <-- lvalue
                      }

                      int main()
                      {
                      X& x = val(); // <-- undefined behavior
                      }

                      would compile fine, but you'd get undefined behavior because val()
                      returns a reference to a local object (destroyed at the end of val()).
                      One way to make this work would be to return a reference and to assign
                      it to a const reference, extending the life of X() to match the
                      reference's:

                      X val() // <-- rvalue
                      {
                      return X(); // <-- rvalue
                      }

                      int main()
                      {
                      const X& x = val(); // ok, const-ref bound to rvalue
                      }

                      To be able to modify the object, you must copy it:

                      int main()
                      {
                      X x = val(); // ok
                      }


                      Jonathan

                      Comment

                      • Victor Bazarov

                        #12
                        Re: Returned object assigned to reference

                        Jonathan Mcdougall wrote:[color=blue]
                        > rageratwork@gma il.com wrote:[color=green]
                        >> Alf P. Steinbach wrote:[color=darkred]
                        >>> * Alf P. Steinbach:
                        >>>> * Old Wolf:
                        >>>>> Alf P. Steinbach wrote:
                        >>>>>> * Andrew Ward:
                        >>>>>>> X val() { return X(); }
                        >>>>>>>
                        >>>>>>> int main()
                        >>>>>>> {
                        >>>>>>> X & x = val();
                        >>>>>>>
                        >>>>>>> What is the lifetime of the object returned by the call to
                        >>>>>>> val()? I would have thought it should be destroyed immediately,
                        >>>>>>> creating a dangling reference, yet the program works, does the
                        >>>>>>> returned object live until the end of it's enclosing scope?
                        >>>>>> Anything, because this is Undefined Behavior.
                        >>>>>
                        >>>>> Is a diagnostic required?
                        >>>>
                        >>>> Undefined Behavior does not require a diagnostic: it's Undefined
                        >>>> what happens... ;-)
                        >>>
                        >>> Hey, wait, that's true, but the function doesn't return a reference.
                        >>>
                        >>> So it's not UB, it's simply something that should not compile (i.e.
                        >>> a diagnostic is required).
                        >>>
                        >>> I thought this was something that compiled.[/color]
                        >>
                        >> I thought this was legal and a temporary copy of X was returned from
                        >> val() to satisfy the rhs of operator=(). Is this not so?[/color]
                        >
                        > No. val() returns an rvalue and it is bound to a non-const reference,
                        > which is illegal. With
                        >
                        > X& val() // <-- reference
                        > {
                        > return X(); // <-- rvalue
                        > }
                        >
                        > int main()
                        > {
                        > X& x = val(); // <-- ok
                        > }
                        >
                        > the assignment would compile (though having undefined behavior), but
                        > then the return statement would not compile (X() is an rvalue, so the
                        > same problem arises). Finally,
                        >
                        > X& val() // <-- reference
                        > {
                        > X x;
                        > return x; // <-- lvalue
                        > }
                        >
                        > int main()
                        > {
                        > X& x = val(); // <-- undefined behavior
                        > }
                        >
                        > would compile fine, but you'd get undefined behavior because val()
                        > returns a reference to a local object (destroyed at the end of val()).
                        > One way to make this work would be to return a reference[/color]

                        [Ahem]... "to return an _object_"
                        [color=blue]
                        > and to assign
                        > it to a const reference,[/color]

                        "and initialise a reference to const with it"
                        [color=blue]
                        > extending the life of X() to match the
                        > reference's:
                        >
                        > X val() // <-- rvalue
                        > {
                        > return X(); // <-- rvalue
                        > }
                        >
                        > int main()
                        > {
                        > const X& x = val(); // ok, const-ref bound to rvalue
                        > }
                        >
                        > To be able to modify the object, you must copy it:
                        >
                        > int main()
                        > {
                        > X x = val(); // ok
                        > }[/color]

                        V
                        --
                        Please remove capital 'A's when replying by e-mail
                        I do not respond to top-posted replies, please don't ask


                        Comment

                        • Old Wolf

                          #13
                          Re: Returned object assigned to reference

                          Alf P. Steinbach wrote:[color=blue][color=green]
                          >> * Old Wolf:[color=darkred]
                          >>> Alf P. Steinbach wrote:
                          >>>> * Andrew Ward:
                          >>>>> X val() { return X(); }
                          >>>>>
                          >>>>> int main()
                          >>>>> {
                          >>>>> X & x = val();
                          >>>>>
                          >>>> this is Undefined Behavior.
                          >>>
                          >>> Is a diagnostic required?[/color][/color]
                          >
                          > So it's not UB, it's simply something that should not compile (i.e. a
                          > diagnostic is required).[/color]

                          ..... hence causing UB due to the constraint violation. But some
                          UBs don't require a diagnostic. In fact the text I was looking for is
                          in 1.4#8:

                          Implementations are required to diagnose programs
                          that use such extensions that are ill-formed
                          according to this International Standard.

                          The standard does not actually say "binding a temporary to a
                          non-const reference causes UB". It just says that a temporary
                          may be bound to a const reference, and does not comment on
                          the notion of binding a temporary to a non-const reference at all.

                          Hence the attempt to bind a temporary to a non-const reference
                          is not well-formed since no rule allows it.

                          Comment

                          • Old Wolf

                            #14
                            Re: Returned object assigned to reference

                            Jonathan Mcdougall wrote:[color=blue]
                            >
                            > X val() // <-- rvalue
                            > {
                            > return X(); // <-- rvalue
                            > }
                            >
                            > int main()
                            > {
                            > const X& x = val(); // ok, const-ref bound to rvalue
                            > }
                            >
                            > To be able to modify the object, you must copy it:[/color]

                            I think it is legal to modify it with a const-cast:

                            X &y = const_cast<X &>(x);

                            Temporary objects are not const per se.

                            Comment

                            • Jonathan Mcdougall

                              #15
                              Re: Returned object assigned to reference

                              Old Wolf wrote:[color=blue]
                              > Jonathan Mcdougall wrote:[color=green]
                              > >
                              > > X val() // <-- rvalue
                              > > {
                              > > return X(); // <-- rvalue
                              > > }
                              > >
                              > > int main()
                              > > {
                              > > const X& x = val(); // ok, const-ref bound to rvalue
                              > > }
                              > >
                              > > To be able to modify the object, you must copy it:[/color]
                              >
                              > I think it is legal to modify it with a const-cast:
                              >
                              > X &y = const_cast<X &>(x);
                              >
                              > Temporary objects are not const per se.[/color]

                              Correct, although this is not something I would do myself, there`s
                              some... elegance missing here :) But as long as an object wasn't
                              declared as const, you may modify it. Note that this applies to
                              objects, but built-in types are not objects, so this:

                              const int& i = 8;
                              const_cast<int& >(i) = 2;

                              is undefined behavior, but

                              class X
                              {
                              public:
                              void f();
                              };

                              const X& x = X();
                              const_cast<X&>( x).f();

                              is perfectly well-defined.

                              Jonathan

                              Comment

                              Working...