Temporary objects, l-values.

Discussion in 'C++' started by jason.cipriani@gmail.com, Feb 19, 2008.

  1. Guest

    First I have a question about the "official" definition of a
    "temporary object".

    So in this code:

    void function1 (SomeThing a) { }

    void function2 (void) {
    SomeThing b;
    function1(b);
    }

    A copy of b is made to pass to function1(). That's a temporary object,
    of course. I was always under the impression that a "temporary object"
    was an object created by the compiler implicitly, and it's entirely
    "behind the scenes" (as in the above example). But, is the object
    created in the following code also called a "temporary object":

    void function1 (SomeThing &a) { }

    void function2 (void) {
    function1(SomeThing());
    }

    I mean, you explicitly construct it. It's not secretly created by the
    compiler -- but it is anonymous (outside of function1), and it does
    get destroyed after the statement is evaluated. It's just not
    completely "behind the scenes". Would that also be called, officially,
    a "temporary object"?

    My second question is about l-values. In the following code:

    struct A {
    A (void) { }
    };

    void function (void) {
    A a;
    A() = a;
    }

    That compiles on all the compilers I've tested it on (Borland's BCC32,
    GCC, and Comeau); and I expected it to. That would mean that "A()" is
    not a temporary, by definition, since it is also an l-value. It seems
    weird that I can say "A() = something;". I mean I know exactly what it
    is doing, but it doesn't make sense to do it (just like trying to set
    "2 = 3" doesn't really make much sense) (unless the = operator has
    some other side effects that you want). Are the compilers I've tried
    it on broken? Or is A() really an l-value there? Or is it compiler-
    specific whether or not that is accepted or not? The reason I am
    confused is because of code like this (untested, I'm just typing it in
    this email hopefully I didn't screw up something important):

    struct X {
    };

    struct Y {
    Y (const X &) { }
    };

    void function (Y &) { }

    void function2 (void) {
    X x;
    function(Y(x));
    }

    That code compiles with recent versions of GCC, Borland's compiler,
    and the MS compiler. However, Comeau does not compile it, complaining
    that non-const reference parameters to functions must be l-values. Yet
    Comeau does compile the following (add a default constructor to Y in
    the above example for this):

    void function2 (void) {
    Y y;
    (Y(x)) = y;
    }

    Comeau's inconsistency about what it thinks an l-value is bugs me (I
    asked them about this one and am waiting for a response).

    So I'm a little confused. Mostly I'm asking to clear up some points in
    a discussion I'm having with somebody on another newsgroup.

    Thanks,
    Jason
     
    , Feb 19, 2008
    #1
    1. Advertising

  2. Kai-Uwe Bux Guest

    wrote:

    > First I have a question about the "official" definition of a
    > "temporary object".
    >
    > So in this code:
    >
    > void function1 (SomeThing a) { }
    >
    > void function2 (void) {
    > SomeThing b;
    > function1(b);
    > }
    >
    > A copy of b is made to pass to function1(). That's a temporary object,
    > of course. I was always under the impression that a "temporary object"
    > was an object created by the compiler implicitly, and it's entirely
    > "behind the scenes" (as in the above example). But, is the object
    > created in the following code also called a "temporary object":
    >
    > void function1 (SomeThing &a) { }
    >
    > void function2 (void) {
    > function1(SomeThing());
    > }
    >
    > I mean, you explicitly construct it. It's not secretly created by the
    > compiler -- but it is anonymous (outside of function1), and it does
    > get destroyed after the statement is evaluated. It's just not
    > completely "behind the scenes". Would that also be called, officially,
    > a "temporary object"?


    a) Yes.

    b) The code should not compile. The temporary does not bind to non-const
    references. See [8.5.3/5].


    > My second question is about l-values. In the following code:
    >
    > struct A {
    > A (void) { }
    > };
    >
    > void function (void) {
    > A a;
    > A() = a;
    > }
    >
    > That compiles on all the compilers I've tested it on (Borland's BCC32,
    > GCC, and Comeau); and I expected it to. That would mean that "A()" is
    > not a temporary, by definition, since it is also an l-value.


    Nope. You seem to think that something needs to be an l-value to allow for
    assignment. That is not true for class types. For class types, the
    assignment operator is a member function. You can call non-const member
    functions on r-values.


    > It seems
    > weird that I can say "A() = something;". I mean I know exactly what it
    > is doing, but it doesn't make sense to do it (just like trying to set
    > "2 = 3" doesn't really make much sense) (unless the = operator has
    > some other side effects that you want).


    Huh? The reason that 2 = 3 does not make sense is that 2 is const. A() is
    not const. Why should it not make sense to set its value?

    > Are the compilers I've tried
    > it on broken? Or is A() really an l-value there? Or is it compiler-
    > specific whether or not that is accepted or not? The reason I am
    > confused is because of code like this (untested, I'm just typing it in
    > this email hopefully I didn't screw up something important):
    >
    > struct X {
    > };
    >
    > struct Y {
    > Y (const X &) { }
    > };
    >
    > void function (Y &) { }
    >
    > void function2 (void) {
    > X x;
    > function(Y(x));
    > }
    >
    > That code compiles with recent versions of GCC, Borland's compiler,
    > and the MS compiler. However, Comeau does not compile it, complaining
    > that non-const reference parameters to functions must be l-values.


    a) gcc also does not compile the code.
    b) Comeau is correct.

    > Yet
    > Comeau does compile the following (add a default constructor to Y in
    > the above example for this):
    >
    > void function2 (void) {
    > Y y;
    > (Y(x)) = y;
    > }
    >
    > Comeau's inconsistency about what it thinks an l-value is bugs me (I
    > asked them about this one and am waiting for a response).
    >
    > So I'm a little confused. Mostly I'm asking to clear up some points in
    > a discussion I'm having with somebody on another newsgroup.


    Contrary to popular belief, temporaries are not const (unless created
    const). However, temporaries do not bind to non-const references. Hence:

    typeded std::vector< int > int_vector;
    int_vector x;

    x.swap( int_vector() ); // illegal
    swap( x, int_vector() ); // illegal
    int_vector().swap( x ); // legal

    In the same way, you can call the assignment operator on a temporary.
    (Usually, there is no point in doing so since all effects of the assignment
    will be lost at the end of the full expression.)



    What is the underlying problem that you are trying to solve?


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Feb 19, 2008
    #2
    1. Advertising

  3. Guest

    Thanks for the quick reply!

    All right, so... now I have lots of nitpicky questions...

    "Kai-Uwe Bux" <> wrote in message
    news:fpe7i8$trk$...
    > wrote:
    >> Would that also be called, officially,
    >> a "temporary object"?

    >
    > a) Yes.


    Ok. Now if you have this:

    {
    SomeThing x;
    function1(x);
    }

    There, x itself is not a "temporary object", right, because you
    explicitly
    declare it, then use it in another statement -- even though it has a
    very
    short life. But if you "shorten" that to this:

    {
    function1(SomeThing());
    }

    Then that SomeThing you pass to function1 *is* a "temporary object".
    The
    line is when you ... define the object explicitly in a separate
    statement,
    so it's lifetime is longer than just the expression it was defined in?
    Do I
    have that right?

    > b) The code should not compile. The temporary does not bind to non-const
    > references. See [8.5.3/5].


    Thanks for the section reference. I'll read through it tomorrow, it
    should
    clear up my confusion.

    > Nope. You seem to think that something needs to be an l-value to allow for
    > assignment. That is not true for class types. For class types, the
    > assignment operator is a member function. You can call non-const member
    > functions on r-values.


    I'm sorry, I don't understand. I'm having a hard time thinking about
    this.
    When I see people describe what an l-value is, it is usually described
    as
    "something that can appear on the left side of an = sign". I take that
    to
    mean "something that can be set equal to something else".

    So what you are saying is: Because the assignment operator is a
    member
    function for class types, a class type does not necessarily have to be
    an
    "l-value" to appear on the left side of an = sign, because it's just
    like
    calling any other member function. Since I can do
    "SomeThing().SomeMemberFunction();", I can do "SomeThing().operator =
    (...);" as well, even though it's not an l-value. Right?

    The reason that I thought SomeThing() was an l-value is because it
    could
    appear on the "left side of an = sign". But, I guess the definition
    is
    deeper than that. So... what does it take for a class type value to be
    an
    l-value then? If the fact that the = operator is just another member
    function like any other means that "SomeThing() = ...;" does not imply
    that
    SomeThing() is necessarily an l-value, then it seems like the "=" sign
    and
    the "l-value-ness" of a class type are completely unrelated concepts?
    In
    other words, if "SomeThing()" can appear on the left side of an =
    sign, but
    it is not an l-value, then what *is* an l-value (since being able to
    be to
    the left of an = is not the only requirement)?

    Is it more accurate to say an l-value is "something that can be set
    equal to
    something else", and not worry about the "=" at all... so it's more
    about
    the meaning of the statement? What I mean is, if you have a type and
    you
    implement an = operator for it that does something else that isn't
    assignment, then in that case "=" has nothing to do with l-values.
    Similarily, if you have a member function, say, "Assign()" that does
    do
    assignment, then an l-value of that type is a value where it's
    meaningful to
    call "Assign()" on, even though the assignment operator itself is not
    involved?

    I am not disagreeing at all -- I'm just trying to wrap my head around
    what
    you typed, because I'm pretty easily confused.

    > Huh? The reason that 2 = 3 does not make sense is that 2 is const. A() is
    > not const. Why should it not make sense to set its value?


    I'm sorry, "2 = 3" was a really bad example that I used to try to
    describe
    something that I didn't know the word for. What I meant was something
    like
    "in most situations, it's a silly thing to do" (for lack of a better
    phrase). Sort of like... assuming the = operator for a type has no
    side
    effects, then doing something like "A() = whatever;" doesn't have any
    effect
    on your program. If you removed it, everything would still be the
    same.
    That's kind of more what I meant. It was a very bad example.

    > a) gcc also does not compile the code.


    > b) Comeau is correct.


    It does not compile with GCC... I must have confused myself with
    different
    test cases. Argh. Thanks for double-checking that.

    > Contrary to popular belief, temporaries are not const (unless created
    > const). However, temporaries do not bind to non-const references.


    I did not think temporaries were const. I think I went the extreme in
    the
    other direction, trying to assign temporaries to other things. My
    logic was
    all screwed up. This is what I had thought:

    1) L-values are things that appear on the left side of = operators.
    2) "(Y(x))" can appear on the left side of = operator.
    3) Temporaries are not l-values. Temporaries can not be assigned to
    other
    things.
    4) Therefore, Y(x) is not a "temporary".
    5) Therefore, Y(x) can bind to a non-const reference.

    But the flaw in that "proof" (of sorts) is that pretty much every
    point is
    wrong :( . Because like you said above, the = operator is just
    another
    non-const member function for class types, nothing special about it.
    And
    temporaries are not necessarily const.

    > Hence:
    >
    > typeded std::vector< int > int_vector;
    > int_vector x;
    >
    > x.swap( int_vector() ); // illegal
    > swap( x, int_vector() ); // illegal
    > int_vector().swap( x ); // legal
    >
    > In the same way, you can call the assignment operator on a temporary.


    If you can call the assignment operator on a temporary, why are you
    not
    allowed to bind temporaries to non-const reference parameters? I mean,
    code
    like this *seems* entirely reasonable to me:

    void function (A &a) {
    // do some stuff to a here
    }

    void function2 (void) {
    function(A());
    }

    Why is that illegal? You create a new A(), pass a reference to it to
    function (so it's not copied when you call function(), it's created
    before
    the actual call is made), it's valid inside function(), you can do
    stuff to
    it, function() returns, statement ends, A() is destroyed. It doesn't
    seem
    dangerous, unpredictable, indeterminant, or anything. Do you know why
    you
    aren't allowed to do that, then (mostly for my own curiosity, I don't
    actually have a program that I "need" to do something like that in)?

    And since temporaries are not necessarily const, why can they (even
    the
    non-const ones) only be bound to const references?

    > (Usually, there is no point in doing so since all effects of the
    > assignment
    > will be lost at the end of the full expression.)


    *That* is what I was trying to say with the stupid "2=3" example. "No
    point". Duh.

    > What is the underlying problem that you are trying to solve?


    It's an off-topic discussion on a Borland newsgroup about exactly
    this
    topic. Whether or not you can bind A() to a non-const reference
    parameter.
    The source of the confusion is two things:

    1) I'm not familiar enough with the standard to know what the real,
    correct
    behavior is, and
    2) The Borland compiler *does*, in fact, accept the above code (with
    "function(A());").

    There was some confusion about which was correct so I asked here to
    get a
    real answer. The Borland compiler is wrong, of course. It's strange
    because
    with the [broken] Borland compiler, the following is accepted, like I
    said:

    struct X { };

    struct Y {
    Y (const X &) { }
    };

    void function (Y &) { }

    void function2 (void) {
    X x;
    function(Y(x));
    function((Y)x);
    }

    But this is not (complaining about binding l-values to non-const
    references):

    struct Y {
    };

    struct X {
    operator Y () { return Y(); }
    };

    void function (Y &) { }

    void function2 (void) {
    X x;
    function(Y(x));
    function((Y)x);
    }

    The difference being that in the first example the conversion uses
    Y's
    constructor, but in the second the conversion uses X's cast operator.
    It's
    totally weird.

    Thanks again for your time!
    Jason
     
    , Feb 19, 2008
    #3
  4. Guest

    Sorry about the screwed up line breaks in that last post... don't
    blame me, blame it on the fact that computers have been around for a
    half a century and nobody's figured out a consistent way to let me
    copy and paste text from one text box to another without problems yet.
     
    , Feb 19, 2008
    #4
  5. Kai-Uwe Bux Guest

    wrote:

    > Thanks for the quick reply!
    >
    > All right, so... now I have lots of nitpicky questions...
    >
    > "Kai-Uwe Bux" <> wrote in message
    > news:fpe7i8$trk$...
    >> wrote:
    >>> Would that also be called, officially,
    >>> a "temporary object"?

    >>
    >> a) Yes.

    >
    > Ok. Now if you have this:
    >
    > {
    > SomeThing x;
    > function1(x);
    > }
    >
    > There, x itself is not a "temporary object", right, because you
    > explicitly
    > declare it, then use it in another statement -- even though it has a
    > very
    > short life. But if you "shorten" that to this:
    >
    > {
    > function1(SomeThing());
    > }
    >
    > Then that SomeThing you pass to function1 *is* a "temporary object".


    Yes.

    > The
    > line is when you ... define the object explicitly in a separate
    > statement,
    > so it's lifetime is longer than just the expression it was defined in?


    Yes. The lifetime of a temporary ends at the full expression with a few
    exceptions listed in [12.24].

    > Do I
    > have that right?


    Huh? The right to do what?

    [snip]

    >> Nope. You seem to think that something needs to be an l-value to allow
    >> for assignment. That is not true for class types. For class types, the
    >> assignment operator is a member function. You can call non-const member
    >> functions on r-values.

    >
    > I'm sorry, I don't understand. I'm having a hard time thinking about
    > this.
    > When I see people describe what an l-value is, it is usually described
    > as
    > "something that can appear on the left side of an = sign". I take that
    > to
    > mean "something that can be set equal to something else".


    One problem is that there is no short definition of l-value and r-value. The
    way it formally works is that chapter 5 describes the evaluation of
    expressions. For each operator, cast, function call, etc. it describes the
    conditions under which it returns an l-value. This yields a recursive
    definition that will allow you to deduce for each expression whether it
    yields an l-value.

    What you heard about l-values being the things that can go on the left
    of "=" probably arises from the line

    There are several assignment operators, all of which group right-to-left.
    All require a modifiable lvalue as their left operand, and the type of an
    assignment expression is that of its left operand. [5.17/1]

    However, for class types, an expression of the for lhs = rhs does not
    invoke an assignment operator but the copy assignment operator.


    > So what you are saying is: Because the assignment operator is a
    > member
    > function for class types, a class type does not necessarily have to be
    > an
    > "l-value" to appear on the left side of an = sign, because it's just
    > like
    > calling any other member function. Since I can do
    > "SomeThing().SomeMemberFunction();", I can do "SomeThing().operator =
    > (...);" as well, even though it's not an l-value. Right?


    Yup, that's what I say. May others correct it if I happen to be wrong.


    > The reason that I thought SomeThing() was an l-value is because it
    > could
    > appear on the "left side of an = sign". But, I guess the definition
    > is
    > deeper than that. So... what does it take for a class type value to be
    > an
    > l-value then? If the fact that the = operator is just another member
    > function like any other means that "SomeThing() = ...;" does not imply
    > that
    > SomeThing() is necessarily an l-value, then it seems like the "=" sign
    > and
    > the "l-value-ness" of a class type are completely unrelated concepts?
    > In
    > other words, if "SomeThing()" can appear on the left side of an =
    > sign, but
    > it is not an l-value, then what *is* an l-value (since being able to
    > be to
    > the left of an = is not the only requirement)?


    Maybe the closest nut-shell definition of l-value is "an object whose
    address can be taken". However, there are, of course issues with this, too:
    operator& is also overloadable.


    > Is it more accurate to say an l-value is "something that can be set
    > equal to
    > something else", and not worry about the "=" at all... so it's more
    > about
    > the meaning of the statement? What I mean is, if you have a type and
    > you
    > implement an = operator for it that does something else that isn't
    > assignment, then in that case "=" has nothing to do with l-values.
    > Similarily, if you have a member function, say, "Assign()" that does
    > do
    > assignment, then an l-value of that type is a value where it's
    > meaningful to
    > call "Assign()" on, even though the assignment operator itself is not
    > involved?


    No. Those are technically unrelated to l-valueness. Note that for class
    types you can assign to temporaries. That does not make them l-values.


    > I am not disagreeing at all -- I'm just trying to wrap my head around
    > what
    > you typed, because I'm pretty easily confused.
    >
    >> Huh? The reason that 2 = 3 does not make sense is that 2 is const. A() is
    >> not const. Why should it not make sense to set its value?

    >
    > I'm sorry, "2 = 3" was a really bad example that I used to try to
    > describe
    > something that I didn't know the word for. What I meant was something
    > like
    > "in most situations, it's a silly thing to do" (for lack of a better
    > phrase). Sort of like... assuming the = operator for a type has no
    > side
    > effects, then doing something like "A() = whatever;" doesn't have any
    > effect
    > on your program. If you removed it, everything would still be the
    > same.
    > That's kind of more what I meant. It was a very bad example.


    Well, that depends. Here is another weird example:

    typedef std::vector< int > int_vector;
    int_vector x;

    swap( x, int_vector() ); // illegal
    swap( x, int_vector() = int_vector() ); // legal

    So, using A() = A(), you can bind a temporary to a non-const reference. Such
    trickery can be useful at times (not often!). The reason is that most
    classes follow the precedence of the built-in operator= and return a
    reference to *this.


    [snip]


    >> Hence:
    >>
    >> typeded std::vector< int > int_vector;
    >> int_vector x;
    >>
    >> x.swap( int_vector() ); // illegal
    >> swap( x, int_vector() ); // illegal
    >> int_vector().swap( x ); // legal
    >>
    >> In the same way, you can call the assignment operator on a temporary.

    >
    > If you can call the assignment operator on a temporary, why are you
    > not
    > allowed to bind temporaries to non-const reference parameters? I mean,
    > code
    > like this *seems* entirely reasonable to me:
    >
    > void function (A &a) {
    > // do some stuff to a here
    > }
    >
    > void function2 (void) {
    > function(A());
    > }


    It is totally reasonable. The problems stem from "interaction of language
    features (misfeatures)".


    > Why is that illegal? You create a new A(), pass a reference to it to
    > function (so it's not copied when you call function(), it's created
    > before
    > the actual call is made), it's valid inside function(), you can do
    > stuff to
    > it, function() returns, statement ends, A() is destroyed. It doesn't
    > seem
    > dangerous, unpredictable, indeterminant, or anything. Do you know why
    > you
    > aren't allowed to do that, then (mostly for my own curiosity, I don't
    > actually have a program that I "need" to do something like that in)?


    The reason that is usually given centers around problems arising from
    impicit conversions. Consider:

    void inc ( int & i ) {
    i += 1;
    }

    double x;
    inc( x );

    If the double converts silently to a temporary int and that binds and gets
    modified, the results would be surprising.


    In any case, C++0X will introduce r-value references that allow temporaries
    to bind.


    > And since temporaries are not necessarily const, why can they (even
    > the
    > non-const ones) only be bound to const references?


    To avoid the surprises from automatic conversions.



    >> (Usually, there is no point in doing so since all effects of the
    >> assignment
    >> will be lost at the end of the full expression.)

    >
    > *That* is what I was trying to say with the stupid "2=3" example. "No
    > point". Duh.
    >
    >> What is the underlying problem that you are trying to solve?

    >
    > It's an off-topic discussion on a Borland newsgroup about exactly
    > this
    > topic. Whether or not you can bind A() to a non-const reference
    > parameter.
    > The source of the confusion is two things:
    >
    > 1) I'm not familiar enough with the standard to know what the real,
    > correct
    > behavior is, and
    > 2) The Borland compiler *does*, in fact, accept the above code (with
    > "function(A());").
    >
    > There was some confusion about which was correct so I asked here to
    > get a
    > real answer.


    Well, [8.5.3/5] describes the precise rules.


    [snip]


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Feb 19, 2008
    #5
  6. Guest

    "Kai-Uwe Bux" <> wrote in message news:fpedrh$jdj
    $...
    >> Do I
    >> have that right?

    >
    > Huh? The right to do what?


    LOL... I meant "am I correct".

    > Well, that depends. Here is another weird example:
    >
    > typedef std::vector< int > int_vector;
    > int_vector x;
    >
    > swap( x, int_vector() ); // illegal
    > swap( x, int_vector() = int_vector() ); // legal


    You're right. That is a weird example. Look, here is a weirder one.
    You can do this to accomplish the same thing without creating a second
    object or any additional temporaries (aside from whatever swap does):

    template <class T> T& abracadabra (const T& v) {
    return const_cast<T&>(v);
    }

    {
    A a;
    swap(a, abracadabra(A()));
    }

    That compiles with GCC, and Comeau likes it, too. Only two A's are
    created ("a", and the A() passed to swap), and the assignment operator
    is called only twice total (at least with the implementation of swap()
    GCC comes with).

    Thanks a lot for your replies, you answered pretty much all my
    questions, the rest I'll figure out once I dig into those sections. I
    appreciated it!

    Jason
     
    , Feb 19, 2008
    #6
  7. Kai-Uwe Bux Guest

    wrote:

    > "Kai-Uwe Bux" <> wrote in message news:fpedrh$jdj
    > $...
    >>> Do I
    >>> have that right?

    >>
    >> Huh? The right to do what?

    >
    > LOL... I meant "am I correct".
    >
    >> Well, that depends. Here is another weird example:
    >>
    >> typedef std::vector< int > int_vector;
    >> int_vector x;
    >>
    >> swap( x, int_vector() ); // illegal
    >> swap( x, int_vector() = int_vector() ); // legal

    >
    > You're right. That is a weird example. Look, here is a weirder one.
    > You can do this to accomplish the same thing without creating a second
    > object or any additional temporaries (aside from whatever swap does):
    >
    > template <class T> T& abracadabra (const T& v) {
    > return const_cast<T&>(v);
    > }


    Ah, the infamous lvalue_cast.


    > {
    > A a;
    > swap(a, abracadabra(A()));
    > }
    >
    > That compiles with GCC, and Comeau likes it, too. Only two A's are
    > created ("a", and the A() passed to swap), and the assignment operator
    > is called only twice total (at least with the implementation of swap()
    > GCC comes with).


    It compiles, but it does have undefined behavior. The reason is explained
    here:

    http://groups.google.com/group/comp.lang.c /browse_frm/thread/e8c8fb5450c8182d


    However, it will have defined behavior in C++0X according to n2521.


    [snip]

    Best
     
    Kai-Uwe Bux, Feb 19, 2008
    #7
  8. Guest

    On Feb 19, 7:29 am, Kai-Uwe Bux <> wrote:
    > Ah, the infamous lvalue_cast.


    Everybody else is always two steps ahead of me!

    > It compiles, but it does have undefined behavior. The reason is explained
    > here:


    Hm, what was an interesting read. What about the following code, is
    that defined? I can't imagine a compiler that would be smart enough to
    recognize what is going on and create some intermediate object in
    between... assuming you haven't overloaded any relevant operators:

    template <class T> T& abracadabra (const T& v) {
    T *pr;
    const T *pv = &v;
    memcpy(&pr, &pv, sizeof(T *));
    return *pr;
    }

    Or this... which I guess is different than the above because here you
    are still casting away the const, so the compiler could figure out
    what is happening (since you aren't hiding it through memcpy)?

    template <class T> T& abracadabra (const T& v) {
    return *(T *)&v;
    }

    Jason
     
    , Feb 19, 2008
    #8
  9. Guest

    On Feb 19, 8:16 am, ""
    <> wrote:
    > template <class T> T& abracadabra (const T& v) {
    > T *pr;
    > const T *pv = &v;
    > memcpy(&pr, &pv, sizeof(T *));
    > return *pr;
    > }


    Rather:

    template <class T> T& abracadabra (const T& v) {
    union {
    T *pr;
    const T* pv;
    } u;
    u.pv = &v;
    return *u.pr;
    }
     
    , Feb 19, 2008
    #9
  10. wrote:
    > On Feb 19, 8:16 am, ""
    > <> wrote:
    >> template <class T> T& abracadabra (const T& v) {
    >> T *pr;
    >> const T *pv = &v;
    >> memcpy(&pr, &pv, sizeof(T *));
    >> return *pr;
    >> }

    >
    > Rather:
    >
    > template <class T> T& abracadabra (const T& v) {
    > union {
    > T *pr;
    > const T* pv;
    > } u;
    > u.pv = &v;
    > return *u.pr;


    Accessing a member of a union that you didn't store in it has
    undefined behaviour. Why don't you simply const_cast it?

    > }


    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Feb 19, 2008
    #10
  11. On 2008-02-19 11:36, wrote:
    > If you can call the assignment operator on a temporary, why are you
    > not allowed to bind temporaries to non-const reference parameters? I
    > mean, code like this *seems* entirely reasonable to me:
    >
    > void function (A &a) {
    > // do some stuff to a here
    > }
    >
    > void function2 (void) {
    > function(A());
    > }
    >
    > Why is that illegal? You create a new A(), pass a reference to it to
    > function (so it's not copied when you call function(), it's created
    > before the actual call is made), it's valid inside function(), you
    > can do stuff to it, function() returns, statement ends, A() is
    > destroyed. It doesn't seem dangerous, unpredictable, indeterminant,
    > or anything. Do you know why you aren't allowed to do that, then
    > (mostly for my own curiosity, I don't actually have a program that I
    > "need" to do something like that in)?
    >
    > And since temporaries are not necessarily const, why can they (even
    > the non-const ones) only be bound to const references?


    While it would work to allow to bind (non-const) references to temporary
    objects in the above code it would not work in many other situations
    that would also be allowed. Consider the following:

    class Foo {
    int i;
    public:
    void print() {
    std::cout << i << std::endl;
    }
    };

    void bar() {
    Foo& f = Foo();
    f.print();
    }

    In this example f is referring to a non-existing object when pring() is
    called (it is a so called dangling reference). Binding temporaries to
    const references is a special rule in C++, what happens is that the life
    of the temporary object is extended to be as long as the life of the
    reference it is bound to.

    --
    Erik Wikström
     
    Erik Wikström, Feb 19, 2008
    #11
  12. Kira Yamato Guest

    On 2008-02-19 13:21:53 -0500, Erik Wikström <> said:

    > On 2008-02-19 11:36, wrote:
    >> If you can call the assignment operator on a temporary, why are you
    >> not allowed to bind temporaries to non-const reference parameters? I
    >> mean, code like this *seems* entirely reasonable to me:
    >>
    >> void function (A &a) {
    >> // do some stuff to a here
    >> }
    >>
    >> void function2 (void) {
    >> function(A());
    >> }
    >>
    >> Why is that illegal? You create a new A(), pass a reference to it to
    >> function (so it's not copied when you call function(), it's created
    >> before the actual call is made), it's valid inside function(), you
    >> can do stuff to it, function() returns, statement ends, A() is
    >> destroyed. It doesn't seem dangerous, unpredictable, indeterminant,
    >> or anything. Do you know why you aren't allowed to do that, then
    >> (mostly for my own curiosity, I don't actually have a program that I
    >> "need" to do something like that in)?
    >>
    >> And since temporaries are not necessarily const, why can they (even
    >> the non-const ones) only be bound to const references?

    >
    > While it would work to allow to bind (non-const) references to temporary
    > objects in the above code it would not work in many other situations
    > that would also be allowed. Consider the following:
    >
    > class Foo {
    > int i;
    > public:
    > void print() {
    > std::cout << i << std::endl;
    > }
    > };
    >
    > void bar() {
    > Foo& f = Foo();
    > f.print();
    > }
    >
    > In this example f is referring to a non-existing object when pring() is
    > called (it is a so called dangling reference). Binding temporaries to
    > const references is a special rule in C++, what happens is that the life
    > of the temporary object is extended to be as long as the life of the
    > reference it is bound to.


    Oh. Nice to know. Thanks.

    --

    // kira
     
    Kira Yamato, Feb 19, 2008
    #12
  13. Erik Wikström wrote:
    > [..]
    > While it would work to allow to bind (non-const) references to
    > temporary objects in the above code it would not work in many other
    > situations that would also be allowed. Consider the following:
    >
    > class Foo {
    > int i;
    > public:
    > void print() {
    > std::cout << i << std::endl;
    > }
    > };
    >
    > void bar() {
    > Foo& f = Foo();
    > f.print();
    > }
    >
    > In this example f is referring to a non-existing object when pring()
    > is called (it is a so called dangling reference). Binding temporaries
    > to const references is a special rule in C++, what happens is that
    > the life of the temporary object is extended to be as long as the
    > life of the reference it is bound to.


    What if the "special rule" would include refs to non-const? The
    simple thing about it is that the temporary survives as long as
    the reference to it. [The current rule allows only ref to const,
    but the idea is the same, the temp survives as long as the ref]

    Now, the problem is deeper than just the lifetime of the reference.
    Binding is allowed with type conversion. If the conversion takes
    place, there is another temporary created, to which the reference
    is actually bound. As soon as you allow binding of a non-const
    reference, you run into "what exactly am I changing" here (this is
    the example Dr. Stroustrup uses in his D&E book):

    void foo(int & i)
    {
    i = 42;
    }

    int main()
    {
    double d = 3.14159;
    foo(d); // what's the value of 'd' after this?
    }

    At the time 'foo' is called, a temporary object of type 'int' is
    created, and if the non-const reference is bound to it, the value
    42 is assigned to that temporary object, but 'd' has nothing to
    do with that. So, OOH 'foo' is supposed to change its argument's
    value to 42, OTOH there is no way for it to accomplish that if
    the factual argument is not of type 'int' but of type _convertible_
    to 'int'.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Feb 19, 2008
    #13
  14. Guest

    On Feb 19, 1:20 pm, "Victor Bazarov" <> wrote:
    > wrote:
    > > template <class T> T& abracadabra (const T& v) {
    > > union {
    > > T *pr;
    > > const T* pv;
    > > } u;
    > > u.pv = &v;
    > > return *u.pr;

    >
    > ... Why don't you simply const_cast it?


    I did, in a previous post in this thread. The reason I didn't use
    const_cast here is I am playing around trying to find other ways to do
    that trick that don't involve explicitly casting the const away (which
    also turned out to be undefined).

    On Feb 19, 1:21 pm, Erik Wikström <> wrote:
    > ...
    > void bar() {
    > Foo& f = Foo();
    > f.print();
    > }
    > ...
    > Binding temporaries to
    > const references is a special rule in C++, ...


    Thanks for your reply. That makes sense. It looks like I also
    misunderstood "binding to non-const references" in this context -- I
    had always assumed that the case of function parameters was different
    from the case of reference declarations and assignments. Your example
    makes sense, but to me that seems different than passing a temporary
    as a non-const reference parameter to a function (where no "lifetime
    extensions" need to be made to keep it valid through the entire
    function call). But, I guess function parameters and variable
    declarations all follow the same rules?

    Jason
     
    , Feb 19, 2008
    #14
  15. Kai-Uwe Bux Guest

    wrote:

    > On Feb 19, 7:29 am, Kai-Uwe Bux <> wrote:
    >> Ah, the infamous lvalue_cast.

    >
    > Everybody else is always two steps ahead of me!
    >
    >> It compiles, but it does have undefined behavior. The reason is explained
    >> here:

    >
    > Hm, what was an interesting read. What about the following code, is
    > that defined? I can't imagine a compiler that would be smart enough to
    > recognize what is going on and create some intermediate object in
    > between... assuming you haven't overloaded any relevant operators:
    >
    > template <class T> T& abracadabra (const T& v) {
    > T *pr;
    > const T *pv = &v;
    > memcpy(&pr, &pv, sizeof(T *));
    > return *pr;
    > }
    >
    > Or this... which I guess is different than the above because here you
    > are still casting away the const, so the compiler could figure out
    > what is happening (since you aren't hiding it through memcpy)?
    >
    > template <class T> T& abracadabra (const T& v) {
    > return *(T *)&v;
    > }


    Neither of these has defined behavior. The problem is that the compiler does
    not need to have a reason to create a const copy of the object when
    initializing the reference. It is free to do that on Fridays, or every
    other time, or randomly.

    For some cases, the following works (by forcing a non-const copy):

    template < typename T >
    struct lvalue_cast_ref : public T {

    lvalue_cast_ref ( T const & t )
    : T ( t )
    {}

    operator T& ( void ) {
    return ( *this );
    }

    };

    template < typename T >
    lvalue_cast_ref<T> lvalue_cast ( T const & t_ref ) {
    return ( lvalue_cast_ref<T>( t_ref ) );
    }

    template < typename T >
    T & lvalue_cast ( T & t_ref ) {
    return ( t_ref );
    }


    #include <vector>
    #include <algorithm>
    #include <cassert>

    typedef std::vector< int > int_vector;

    int main ( void ) {
    int_vector x ( 20, 20 );
    swap( x, lvalue_cast( int_vector() ) );
    assert( x.empty() );
    }


    Note however, that the above cannot work for built-in types.
    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Feb 19, 2008
    #15
  16. Guest

    Thanks a lot for your replies.

    On Feb 19, 2:15 pm, Kai-Uwe Bux <> wrote:
    > Neither of these has defined behavior. The problem is that the compiler does
    > not need to have a reason to create a const copy of the object when
    > initializing the reference. It is free to do that on Fridays, or every
    > other time, or randomly.


    I see... so, in other words, no matter what happens inside
    abracadabra(), it's always going to be undefined because of the fact
    that the function takes a const reference in the first place -- and
    there's no rule that says that a const reference actually needs to
    refer to the same instance of the object you bound to it? But if
    that's the case, does that mean that the following is also undefined?

    struct A {
    mutable int v;
    };

    void function (const A &a) {
    a.v = 3;
    }

    void function2 (void) {
    // when calling a function...
    A x;
    function(x);
    assert(x.v == 3); // <--- this may fail?
    // and like this...
    const A &y = x;
    y.v = 4;
    assert(x.v == 4); // <--- this may also fail?
    }

    Because in that situation, if you were typing your code with your
    tongue hanging out the left side of your mouth instead of the right
    and therefore the compiler decided to create a const copy of 'x' when
    you called function(), then it's not necessarily x's 'v' member that
    function() is modifying? Comeau does not warn about anything dangerous
    there, though.

    > For some cases, the following works (by forcing a non-const copy):
    > [snip]


    That was a really enlightening example, thanks. I think... it's
    similar to something I tried when I was experimenting, adding this:

    //...
    WhateverType & self (void) { return *this; }
    //...

    To a WhateverType and then binding WhateverType().self() to non-const
    references instead. Except that doesn't fit in with existing objects
    and flow with existing syntax as nicely.

    Thanks,
    Jason
     
    , Feb 19, 2008
    #16
  17. James Kanze Guest

    On Feb 19, 7:34 pm, "Victor Bazarov" <> wrote:
    > Erik Wikström wrote:
    > > [..]

    > What if the "special rule" would include refs to non-const? The
    > simple thing about it is that the temporary survives as long as
    > the reference to it. [The current rule allows only ref to const,
    > but the idea is the same, the temp survives as long as the ref]


    That is the rule. Any temporary which is used to initialize a
    reference has its lifetime extended to that of the reference.

    There is a second rule involved here: a temporary cannot be used
    to initialize a reference unless that reference is to a const.
    This second rule was added relatively late, however---it isn't
    present in my copy of TC++PL ((c) 1986) for example (but there
    is a statement that temporaries used to initialize references
    have a lifetime of the scope, i.e. the same as the 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, Feb 19, 2008
    #17
  18. James Kanze wrote:
    > On Feb 19, 7:34 pm, "Victor Bazarov" <> wrote:
    >> Erik Wikström wrote:
    >>> [..]

    >> What if the "special rule" would include refs to non-const? The
    >> simple thing about it is that the temporary survives as long as
    >> the reference to it. [The current rule allows only ref to const,
    >> but the idea is the same, the temp survives as long as the ref]

    >
    > That is the rule. Any temporary which is used to initialize a
    > reference has its lifetime extended to that of the reference.
    >
    > There is a second rule involved here: a temporary cannot be used
    > to initialize a reference unless that reference is to a const.
    > This second rule was added relatively late, however---it isn't
    > present in my copy of TC++PL ((c) 1986) for example (but there
    > is a statement that temporaries used to initialize references
    > have a lifetime of the scope, i.e. the same as the reference).


    James,

    I know it's late where you are. But may I please bring the words
    "what if" to your attention? Why are you reiterating what the
    rule _is_ when I am talking about what the rule _might_be_?

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Feb 19, 2008
    #18
  19. Kai-Uwe Bux Guest

    Erik Wikström wrote:

    > On 2008-02-19 11:36, wrote:
    >> If you can call the assignment operator on a temporary, why are you
    >> not allowed to bind temporaries to non-const reference parameters? I
    >> mean, code like this *seems* entirely reasonable to me:
    >>
    >> void function (A &a) {
    >> // do some stuff to a here
    >> }
    >>
    >> void function2 (void) {
    >> function(A());
    >> }
    >>
    >> Why is that illegal? You create a new A(), pass a reference to it to
    >> function (so it's not copied when you call function(), it's created
    >> before the actual call is made), it's valid inside function(), you
    >> can do stuff to it, function() returns, statement ends, A() is
    >> destroyed. It doesn't seem dangerous, unpredictable, indeterminant,
    >> or anything. Do you know why you aren't allowed to do that, then
    >> (mostly for my own curiosity, I don't actually have a program that I
    >> "need" to do something like that in)?
    >>
    >> And since temporaries are not necessarily const, why can they (even
    >> the non-const ones) only be bound to const references?

    >
    > While it would work to allow to bind (non-const) references to temporary
    > objects in the above code it would not work in many other situations
    > that would also be allowed. Consider the following:
    >
    > class Foo {
    > int i;
    > public:
    > void print() {
    > std::cout << i << std::endl;
    > }
    > };
    >
    > void bar() {
    > Foo& f = Foo();
    > f.print();
    > }
    >
    > In this example f is referring to a non-existing object when pring() is
    > called (it is a so called dangling reference). Binding temporaries to
    > const references is a special rule in C++, what happens is that the life
    > of the temporary object is extended to be as long as the life of the
    > reference it is bound to.


    That is not entirely correct. The standard says [12.2/4-5]:

    There are two contexts in which temporaries are destroyed at a different
    point than the end of the fullexpression. The first context is ...

    The second context is when a reference is bound to a temporary. The
    temporary to which the reference is bound or the temporary that is the
    complete object to a subobject of which the temporary is bound persists
    for the lifetime of the reference except as specified below.

    and then a list of exceptions to the rule follows. Note that the life-time
    extension does not depend on the constness or non-constness of the
    reference. In particular, if [8.5.3/5] was worded to allow temporaries to
    bind to non-const references, your code snippet would be perfectly fine.


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Feb 20, 2008
    #19
  20. James Kanze Guest

    On Feb 19, 10:52 pm, "Victor Bazarov" <> wrote:
    > James Kanze wrote:
    > > On Feb 19, 7:34 pm, "Victor Bazarov" <> wrote:
    > >> Erik Wikström wrote:
    > >>> [..]
    > >> What if the "special rule" would include refs to non-const? The
    > >> simple thing about it is that the temporary survives as long as
    > >> the reference to it. [The current rule allows only ref to const,
    > >> but the idea is the same, the temp survives as long as the ref]


    > > That is the rule. Any temporary which is used to initialize a
    > > reference has its lifetime extended to that of the reference.


    > > There is a second rule involved here: a temporary cannot be used
    > > to initialize a reference unless that reference is to a const.
    > > This second rule was added relatively late, however---it isn't
    > > present in my copy of TC++PL ((c) 1986) for example (but there
    > > is a statement that temporaries used to initialize references
    > > have a lifetime of the scope, i.e. the same as the reference).


    > I know it's late where you are. But may I please bring the words
    > "what if" to your attention? Why are you reiterating what the
    > rule _is_ when I am talking about what the rule _might_be_?


    No. You're talking about what the rule _was_.

    As Kai-Uwe has pointed out, there is no special rule concerning
    lifetime for "references to const"---the rule concerns
    temporaries used to initialize a reference. Any reference.
    (From context, I presume that this is the "special rule" you
    were refering to. Given the number of special rules in C++,
    however, one can never be sure:).) In other words, your "what
    if" is already the case.

    --
    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, Feb 20, 2008
    #20
    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. Andy Buckley
    Replies:
    5
    Views:
    345
    Victor Bazarov
    Aug 26, 2004
  2. Ariel
    Replies:
    2
    Views:
    421
    Ariel
    Nov 3, 2004
  3. Replies:
    5
    Views:
    361
    Victor Bazarov
    Jun 24, 2005
  4. 7stud
    Replies:
    11
    Views:
    729
    Dennis Lee Bieber
    Mar 20, 2007
  5. Replies:
    7
    Views:
    3,306
    James Kanze
    Feb 12, 2008
Loading...

Share This Page