GotW #88: Is it safe to const_cast a reference to a temporary?

Discussion in 'C++' started by Niels Dekker - no return address, Feb 2, 2008.

  1. Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    about binding a temporary object to a reference-to-const. Now if this
    temporary isn't const, is it safe to const_cast the reference?

    #include <string>
    #include <cassert>
    using std::string;

    string f() { return "abc"; }

    void h() {
    const string& s1 = f();
    const string& s2 = s1;
    const_cast<string&>(s1) = "new value!"; // Safe?
    assert(s1 == "new value!"); // Okay?
    assert(s2 == "new value!"); // Okay?
    }

    See also:
    GotW #88 - A Candidate For the "Most Important const."
    http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry

    Kind regards,
    --
    Niels Dekker
    http://www.xs4all.nl/~nd/dekkerware
    Scientific programmer at LKEB, Leiden University Medical Center
     
    Niels Dekker - no return address, Feb 2, 2008
    #1
    1. Advertising

  2. * Niels Dekker - no return address:
    > Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    > about binding a temporary object to a reference-to-const. Now if this
    > temporary isn't const, is it safe to const_cast the reference?
    >
    > #include <string>
    > #include <cassert>
    > using std::string;
    >
    > string f() { return "abc"; }
    >
    > void h() {
    > const string& s1 = f();
    > const string& s2 = s1;
    > const_cast<string&>(s1) = "new value!"; // Safe?


    Not in standard C++, although it might be safe with a particular compiler.

    The short of it is that you're misinforming the compiler, and the almost
    as short of it is that the compiler is free to optimize away the call to
    f and e.g. substitute the value string("abc") wherever s1 and s2 are used.

    The bit-longer of it is that the temporary was const to begin with (the
    temporary bound to the reference, not the temporary returned by the
    function, although they might end up being the same), and you can't cast
    away /original/ const'ness with portable well-defined result.


    > assert(s1 == "new value!"); // Okay?
    > assert(s2 == "new value!"); // Okay?
    > }
    >
    > See also:
    > GotW #88 - A Candidate For the "Most Important const."
    > http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry


    Would IMHO be better if Herb posted his GotW questions here, as he did
    for the first few, and summarized the responses + his extra insights
    (AFAIK the PIMPL idiom GOTW with erronous auto_ptr still not corrected).


    Cheers, & hth.,

    - Alf


    --
    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?
     
    Alf P. Steinbach, Feb 2, 2008
    #2
    1. Advertising

  3. * Alf P. Steinbach:
    > * Niels Dekker - no return address:
    >> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    >> about binding a temporary object to a reference-to-const. Now if this
    >> temporary isn't const, is it safe to const_cast the reference?
    >>
    >> #include <string>
    >> #include <cassert>
    >> using std::string;
    >>
    >> string f() { return "abc"; }
    >> void h() {
    >> const string& s1 = f();
    >> const string& s2 = s1;
    >> const_cast<string&>(s1) = "new value!"; // Safe?

    >
    > Not in standard C++, although it might be safe with a particular compiler.
    >
    > The short of it is that you're misinforming the compiler, and the almost
    > as short of it is that the compiler is free to optimize away the call to
    > f and e.g. substitute the value string("abc") wherever s1 and s2 are used.


    Except for taking address, which includes passing by reference, which
    applies to just about anything it could be used for.

    I didn't think of that.

    And I'm not sure there is any qualification of "used" that would make
    the last statement above true.

    Sorry about hasty trigger finger -- correcting this before Someone
    Else does.

    However, rest applies:


    > The bit-longer of it is that the temporary was const to begin with (the
    > temporary bound to the reference, not the temporary returned by the
    > function, although they might end up being the same), and you can't cast
    > away /original/ const'ness with portable well-defined result.
    >
    >
    >> assert(s1 == "new value!"); // Okay?
    >> assert(s2 == "new value!"); // Okay?
    >> }
    >>
    >> See also:
    >> GotW #88 - A Candidate For the "Most Important const."
    >> http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry

    >
    > Would IMHO be better if Herb posted his GotW questions here, as he did
    > for the first few, and summarized the responses + his extra insights
    > (AFAIK the PIMPL idiom GOTW with erronous auto_ptr still not corrected).


    Cheers, & hth.,

    - Alf (wondering why the Send button doesn't say "Do you Really want to
    Send?")

    --
    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?
     
    Alf P. Steinbach, Feb 2, 2008
    #3
  4. > Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    > about binding a temporary object to a reference-to-const. Now if this
    > temporary isn't const, is it safe to const_cast the reference?


    > const string& s1 = f();
    > const_cast<string&>(s1) = "new value!"; // Safe?


    Alf P. Steinbach replied:
    >> Not in standard C++, although it might be safe with a particular compiler.


    Thanks! It looked too much like a hack anyway...

    >> The short of it is that you're misinforming the compiler, and the almost
    >> as short of it is that the compiler is free to optimize away the call to
    >> f and e.g. substitute the value string("abc") wherever s1 and s2 are used.

    >
    > Except for taking address, which includes passing by reference, which
    > applies to just about anything it could be used for.


    Okay, that makes sense.

    But I guess in C++0x we can bind the temporary object returned by f() to
    an rvalue reference:

    string && s1 = f(); // C++0x
    s1 = "new value!";

    Still this wouldn't work if the return type of f() would have been
    declared as "const":
    const string f(void);

    Right?

    Kind regards, Niels
     
    Niels Dekker - no return address, Feb 3, 2008
    #4
  5. Niels Dekker - no return address

    James Kanze Guest

    On Feb 2, 10:37 pm, "Alf P. Steinbach" <> wrote:
    > * Niels Dekker - no return address:


    > > Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    > > about binding a temporary object to a reference-to-const. Now if this
    > > temporary isn't const, is it safe to const_cast the reference?


    > > #include <string>
    > > #include <cassert>
    > > using std::string;


    > > string f() { return "abc"; }


    > > void h() {
    > > const string& s1 = f();
    > > const string& s2 = s1;
    > > const_cast<string&>(s1) = "new value!"; // Safe?


    > Not in standard C++, although it might be safe with a
    > particular compiler.


    This has always been my believe as well, and I'm almost sure
    that I once read somewhere in the standard something to the
    effect that any attempt to modify a temporary (e.g. by such
    tricks) was undefined behavior. The last time I looked for it,
    however, I couldn't find it. So could you tell me where in the
    standard this is forbidden. It's tricky, because you are
    allowed to call a non-const function on a temporary, and the
    assignment operator of std::string is a non-const function.

    There are really two separate questions: what about:
    int const& i = 42 ;
    const_cast< int& >( i ) = 0 ;
    I certainly hope that it's illegal, but that was the case I was
    actually looking for, and couldn't find.

    It's frustrating me, because I really want the example with int
    to be undefined behavior, and I'm sure that it is, but I can't
    find the words in the standard to back it up.

    > The short of it is that you're misinforming the compiler, and
    > the almost as short of it is that the compiler is free to
    > optimize away the call to f and e.g. substitute the value
    > string("abc") wherever s1 and s2 are used.


    This isn't generally true; a compiler cannot assume that because
    a reference to const evaluated to a certain value once, it will
    evaluate to that value a second time. The question is: when can
    it make such assumptions?

    > The bit-longer of it is that the temporary was const to begin
    > with (the temporary bound to the reference, not the temporary
    > returned by the function, although they might end up being the
    > same), and you can't cast away /original/ const'ness with
    > portable well-defined result.


    Except that in the example, the temporary wasn't const to begin
    with. The function f() retuns an std::string, and not an
    std::string const. If f() had returned an std::string const,
    then the code definitely has undefined behavior.

    Again, we have two different cases to consider. Temporaries of
    non-class types are never const.

    > > assert(s1 == "new value!"); // Okay?
    > > assert(s2 == "new value!"); // Okay?
    > > }


    > > See also:
    > > GotW #88 - A Candidate For the "Most Important const."
    > >http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry


    > Would IMHO be better if Herb posted his GotW questions here,
    > as he did for the first few,


    More than just a few:). Certainly, if he wants people to
    comment on them, or even read them, he should post them
    somewhere that people read regularly. Nothing against Herb's
    site, but I don't visit it every week just to see if there's
    anything new.

    --
    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 3, 2008
    #5
  6. * James Kanze:
    > On Feb 2, 10:37 pm, "Alf P. Steinbach" <> wrote:
    >> * Niels Dekker - no return address:

    >
    >>> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    >>> about binding a temporary object to a reference-to-const. Now if this
    >>> temporary isn't const, is it safe to const_cast the reference?

    >
    >>> #include <string>
    >>> #include <cassert>
    >>> using std::string;

    >
    >>> string f() { return "abc"; }

    >
    >>> void h() {
    >>> const string& s1 = f();
    >>> const string& s2 = s1;
    >>> const_cast<string&>(s1) = "new value!"; // Safe?

    >
    >> Not in standard C++, although it might be safe with a
    >> particular compiler.

    >
    > This has always been my believe as well, and I'm almost sure
    > that I once read somewhere in the standard something to the
    > effect that any attempt to modify a temporary (e.g. by such
    > tricks) was undefined behavior. The last time I looked for it,
    > however, I couldn't find it. So could you tell me where in the
    > standard this is forbidden.


    Yes. With respect to the 1998 standard there are two rules involved:
    first, a rule saying that modifying a (we must presume "original" is
    meant) const object incurs Undefined Behavior, §7.1.5.1/4, and second, a
    rule that that the reference can be, at the implementation's discretion,
    a reference to an original const object (a new const temporary that is
    copy constructed from the initializer), §8.5.3/5.

    There is a problem with this, and that is that the relevant part of
    §8.5.3/5, at least as I interpret is, is also the culprit responsible
    for requiring a copy constructor when passing an rvalue of class type to
    T const& argument, and as I recall it has therefore been changed the
    C++0x draft. Checking...

    Yep, in the n2315 draft (I don't have the latest) it has been changed,
    and the reference bound directly, no implementation discretion -- and
    I gather that that otherwise desirable change may just open the door for
    code such as above, in C++0x... And hm, what about std::auto_ptr. :-(


    > It's tricky, because you are
    > allowed to call a non-const function on a temporary, and the
    > assignment operator of std::string is a non-const function.


    I don't think that's relevant. I think you perhaps were thinking of
    only direct binding of the reference, as in the C++0x draft, and that
    what you point out here would indicate that the temporary returned by
    the function is not really const. And it isn't, but the temporary the
    reference is bound to per the C++ 1998 standard, can be original const
    (if a copy is made it is required to be original const).


    > There are really two separate questions: what about:
    > int const& i = 42 ;
    > const_cast< int& >( i ) = 0 ;
    > I certainly hope that it's illegal, but that was the case I was
    > actually looking for, and couldn't find.


    It's the same paragraph, §8.5.3/5, but a different part a bit further
    down. Also here the temporary is original const.


    > It's frustrating me, because I really want the example with int
    > to be undefined behavior, and I'm sure that it is, but I can't
    > find the words in the standard to back it up.


    See above.


    >> The short of it is that you're misinforming the compiler, and
    >> the almost as short of it is that the compiler is free to
    >> optimize away the call to f and e.g. substitute the value
    >> string("abc") wherever s1 and s2 are used.

    >
    > This isn't generally true; a compiler cannot assume that because
    > a reference to const evaluated to a certain value once, it will
    > evaluate to that value a second time. The question is: when can
    > it make such assumptions?


    Note: I corrected the original posting almost immediately (see that
    follow-up), because taking the address of the object is one usage where
    the same object must be involved.

    Per the 1998 standard one case for use of rvalue is as above, where the
    compiler is free to bind the reference to a newly constructed original
    const copy of the initializer, because modifying that original const
    object is UB.



    >> The bit-longer of it is that the temporary was const to begin
    >> with (the temporary bound to the reference, not the temporary
    >> returned by the function, although they might end up being the
    >> same), and you can't cast away /original/ const'ness with
    >> portable well-defined result.

    >
    > Except that in the example, the temporary wasn't const to begin
    > with. The function f() retuns an std::string, and not an
    > std::string const. If f() had returned an std::string const,
    > then the code definitely has undefined behavior.


    In the current standard the constness of f()'s result doesn't matter,
    because it's not necessarily the object the reference is bound to.


    Cheers, & hth.,

    - Alf

    --
    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?
     
    Alf P. Steinbach, Feb 3, 2008
    #6
  7. * Niels Dekker - no return address:
    >
    > But I guess in C++0x we can bind the temporary object returned by f() to
    > an rvalue reference:
    >
    > string && s1 = f(); // C++0x
    > s1 = "new value!";
    >
    > Still this wouldn't work if the return type of f() would have been
    > declared as "const":
    > const string f(void);
    >
    > Right?


    The last about const, yes I think obviously, but the first, I don't
    know: if rvalue references are to be useful that would be one case they
    should be able to handle, but I don't know more about rvalue references
    than the general impression that they can perform some black magic, and
    essentially provide language support for "moving construction", allowing
    you to freely implement functions that return large objects by value.

    Cheers,

    - Alf

    --
    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?
     
    Alf P. Steinbach, Feb 3, 2008
    #7
  8. Niels Dekker - no return address

    James Kanze Guest

    On Feb 3, 3:55 pm, "Alf P. Steinbach" <> wrote:
    > * James Kanze:
    > > On Feb 2, 10:37 pm, "Alf P. Steinbach" <> wrote:
    > >> * Niels Dekker - no return address:


    > >>> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    > >>> about binding a temporary object to a reference-to-const. Now if this
    > >>> temporary isn't const, is it safe to const_cast the reference?


    > >>> #include <string>
    > >>> #include <cassert>
    > >>> using std::string;


    > >>> string f() { return "abc"; }


    > >>> void h() {
    > >>> const string& s1 = f();
    > >>> const string& s2 = s1;
    > >>> const_cast<string&>(s1) = "new value!"; // Safe?


    > >> Not in standard C++, although it might be safe with a
    > >> particular compiler.


    > > This has always been my believe as well, and I'm almost sure
    > > that I once read somewhere in the standard something to the
    > > effect that any attempt to modify a temporary (e.g. by such
    > > tricks) was undefined behavior. The last time I looked for it,
    > > however, I couldn't find it. So could you tell me where in the
    > > standard this is forbidden.


    > Yes. With respect to the 1998 standard there are two rules
    > involved: first, a rule saying that modifying a (we must
    > presume "original" is meant) const object incurs Undefined
    > Behavior, §7.1.5.1/4, and second, a rule that that the
    > reference can be, at the implementation's discretion, a
    > reference to an original const object (a new const temporary
    > that is copy constructed from the initializer), §8.5.3/5.


    I don't see where "orignal" is relevant. This rule concerns the
    object itself, and not the type of expression used to refer to
    it. In the sample code above, none of the objects are const.
    That is, IMHO, the crux of the problem. (And I very definitely
    remember reading somewhere that attempting to modify an
    rvalue---or maybe it was only an rvalue of non-class type---was
    undefined behavior, regardless of the const.)

    > There is a problem with this, and that is that the relevant part of
    > §8.5.3/5, at least as I interpret is, is also the culprit responsible
    > for requiring a copy constructor when passing an rvalue of class type to
    > T const& argument, and as I recall it has therefore been changed the
    > C++0x draft. Checking...


    > Yep, in the n2315 draft (I don't have the latest) it has been changed,
    > and the reference bound directly, no implementation discretion -- and
    > I gather that that otherwise desirable change may just open the door for
    > code such as above, in C++0x... And hm, what about std::auto_ptr. :-(


    > > It's tricky, because you are
    > > allowed to call a non-const function on a temporary, and the
    > > assignment operator of std::string is a non-const function.


    > I don't think that's relevant. I think you perhaps were thinking of
    > only direct binding of the reference, as in the C++0x draft, and that
    > what you point out here would indicate that the temporary returned by
    > the function is not really const. And it isn't, but the temporary the
    > reference is bound to per the C++ 1998 standard, can be original const
    > (if a copy is made it is required to be original const).


    No. I'm thinking of the object itself. The temporary, and not
    the reference. The const in the reference doesn't affect the
    const-ness of the temporary. For example:
    int i = 43 ;
    int const& ri = i ;
    const_cast< int& >( ri ) = 0 ;
    is perfectly defined and legal. (Which, of course, doesn't say
    that it's good code.)

    In the original code, the function returns a non-const object.
    Something like:
    f() = "whatever" ;
    is perfectly legal (although not very useful). So is:
    std::string s( "abc" ) ;
    std::string const& rs = s ;
    static_cast< std::string& >( rs ) = "whatever" ;
    The original code looks very much like a combination of these
    two cases, and in the absense of some special rule, is clearly
    legal and well defined.

    > > There are really two separate questions: what about:
    > > int const& i = 42 ;
    > > const_cast< int& >( i ) = 0 ;
    > > I certainly hope that it's illegal, but that was the case I was
    > > actually looking for, and couldn't find.


    > It's the same paragraph, §8.5.3/5, but a different part a bit
    > further down. Also here the temporary is original const.


    I don't think so. In general, "temporary" and "rvalue" are
    synonyms. And §3.10/9 very clearly says "Class rvalues can have
    cv-qualified types; non-class rvalues always have cv-unqualified
    types." A temporary of type int can never be const.

    > > It's frustrating me, because I really want the example with
    > > int to be undefined behavior, and I'm sure that it is, but I
    > > can't find the words in the standard to back it up.


    > See above.


    I get the feeling that there must be something obvious I'm
    missing, but I just can't see it. The reference must refer to
    const, but references to const can definitly be bound to
    non-const objects, and when they are, casting away const and
    modifying the object is well defined behavior. So we need
    a special rule to forbid modifying the (non-const) temporary.

    > >> The short of it is that you're misinforming the compiler,
    > >> and the almost as short of it is that the compiler is free
    > >> to optimize away the call to f and e.g. substitute the
    > >> value string("abc") wherever s1 and s2 are used.


    > > This isn't generally true; a compiler cannot assume that
    > > because a reference to const evaluated to a certain value
    > > once, it will evaluate to that value a second time. The
    > > question is: when can it make such assumptions?


    > Note: I corrected the original posting almost immediately (see
    > that follow-up), because taking the address of the object is
    > one usage where the same object must be involved.


    That wasn't what I was thinking of. The most obvious example:

    int global = 0 ;

    void
    f( int const& i )
    {
    std::cout << i << std::endl ;
    ++ global ;
    std::cout << i << std::endl ;
    }

    int
    main()
    {
    f( global ) ;
    }

    This program had better output 0, then 1.

    Change f() to:

    void
    f( int const& i )
    {
    std::cout << i << std::endl ;
    ++ const_cast< int& >( i ) ;
    std::cout << i << std::endl ;
    }

    and it's still well defined. Declare "global" const, and the
    above involves undefined behavior, however. The defined-ness
    depends on the const-ness of the object refered to, and not on
    the const-ness of the reference.

    Which brings us back to my original problem: the temporaries
    here don't have a const type---and in the case of non-class
    types, cannot have a const types. So there must be some
    additional rule somewhere which makes attempting to modify them
    undefined behavior.

    > Per the 1998 standard one case for use of rvalue is as above,
    > where the compiler is free to bind the reference to a newly
    > constructed original const copy of the initializer, because
    > modifying that original const object is UB.


    > >> The bit-longer of it is that the temporary was const to begin
    > >> with (the temporary bound to the reference, not the temporary
    > >> returned by the function, although they might end up being the
    > >> same), and you can't cast away /original/ const'ness with
    > >> portable well-defined result.


    > > Except that in the example, the temporary wasn't const to begin
    > > with. The function f() retuns an std::string, and not an
    > > std::string const. If f() had returned an std::string const,
    > > then the code definitely has undefined behavior.


    > In the current standard the constness of f()'s result doesn't
    > matter, because it's not necessarily the object the reference
    > is bound to.


    Which has the consequence that even if f() were declared to
    return a std::string const, the code might have defined
    behavior? (In fact, the standards committee has changed this,
    and I think we can ignore it. The problem that I see is that
    the original temporary object is not const. And so casting away
    const and modifying it is well defined behavior.)

    --
    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 3, 2008
    #8
  9. * James Kanze:
    > On Feb 3, 3:55 pm, "Alf P. Steinbach" <> wrote:
    >> * James Kanze:
    >>> On Feb 2, 10:37 pm, "Alf P. Steinbach" <> wrote:
    >>>> * Niels Dekker - no return address:

    >
    >>>>> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    >>>>> about binding a temporary object to a reference-to-const. Now if this
    >>>>> temporary isn't const, is it safe to const_cast the reference?

    >
    >>>>> #include <string>
    >>>>> #include <cassert>
    >>>>> using std::string;

    >
    >>>>> string f() { return "abc"; }

    >
    >>>>> void h() {
    >>>>> const string& s1 = f();
    >>>>> const string& s2 = s1;
    >>>>> const_cast<string&>(s1) = "new value!"; // Safe?

    >
    >>>> Not in standard C++, although it might be safe with a
    >>>> particular compiler.

    >
    >>> This has always been my believe as well, and I'm almost sure
    >>> that I once read somewhere in the standard something to the
    >>> effect that any attempt to modify a temporary (e.g. by such
    >>> tricks) was undefined behavior. The last time I looked for it,
    >>> however, I couldn't find it. So could you tell me where in the
    >>> standard this is forbidden.

    >
    >> Yes. With respect to the 1998 standard there are two rules
    >> involved: first, a rule saying that modifying a (we must
    >> presume "original" is meant) const object incurs Undefined
    >> Behavior, §7.1.5.1/4, and second, a rule that that the
    >> reference can be, at the implementation's discretion, a
    >> reference to an original const object (a new const temporary
    >> that is copy constructed from the initializer), §8.5.3/5.

    >
    > I don't see where "orignal" is relevant. This rule concerns the
    > object itself, and not the type of expression used to refer to
    > it. In the sample code above, none of the objects are const.
    > That is, IMHO, the crux of the problem.


    No, the reference's type -- its cv-qualification -- is very relevant
    because the cv-qualification can be transferred to a temporary created
    for the purpose, and no, it's not the case that one is guaranteed that
    none of the objects are const, in the code above.

    Trying to be utterly unambigiously clear:

    §5.1.3/5
    "A reference to type "cv1 T1" is initialized by an expression of
    type "cv2 T2" as follows:

    ...

    - Otherwise, the reference shall be to a non-volatile const type
    (i.e. cv1 shall be const)
    [Example: ...]

    - If the initializer expression is an rvalue, with T2 a class
    type, and "cv1 T1" is reference-compatible with "cv2 T2", the
    reference is bound in one of the following ways (the choice is
    implementation-defined):

    - The reference is bound to the object represented by the rvalue
    (see 3.10) or to a sub-object within that object.

    - A temporary of type "cv1 T2" [sic] is created, and a constructor
    is called to copy the entire rvalue into the temporary. The
    reference is bound to the temporary or to sub-object within
    the temporary.

    The last alternative above is the one that, together with UB for
    modification of const, dictates UB for the example code. And what
    happens in this case is that "cv1" is "const", from the declaration of
    the /reference/ s1, "T2" is "string", from the declaration of f(), and
    the type of the temporary created is then "const string" -- which is
    not a typo, but intentional ([sic] means essentially that "this is /not/
    a typo"). This is then what the reference is bound to when this case is
    chosen, namely a new temporary of type "const string".

    And the pure possibility of that alternative being chosen, and that
    alternative implying UB, means that formally the code is UB.


    > (And I very definitely
    > remember reading somewhere that attempting to modify an
    > rvalue---or maybe it was only an rvalue of non-class type---was
    > undefined behavior, regardless of the const.)


    For rvalue of non-class type yes I think so, but for rvalue of class
    type, no, quite the opposite.

    But I've done enough standardeeze for today, I think. ;-)


    >> There is a problem with this, and that is that the relevant part of
    >> §8.5.3/5, at least as I interpret is, is also the culprit responsible
    >> for requiring a copy constructor when passing an rvalue of class type to
    >> T const& argument, and as I recall it has therefore been changed the
    >> C++0x draft. Checking...

    >
    >> Yep, in the n2315 draft (I don't have the latest) it has been changed,
    >> and the reference bound directly, no implementation discretion -- and
    >> I gather that that otherwise desirable change may just open the door for
    >> code such as above, in C++0x... And hm, what about std::auto_ptr. :-(

    >
    >>> It's tricky, because you are
    >>> allowed to call a non-const function on a temporary, and the
    >>> assignment operator of std::string is a non-const function.

    >
    >> I don't think that's relevant. I think you perhaps were thinking of
    >> only direct binding of the reference, as in the C++0x draft, and that
    >> what you point out here would indicate that the temporary returned by
    >> the function is not really const. And it isn't, but the temporary the
    >> reference is bound to per the C++ 1998 standard, can be original const
    >> (if a copy is made it is required to be original const).

    >
    > No. I'm thinking of the object itself. The temporary, and not
    > the reference. The const in the reference doesn't affect the
    > const-ness of the temporary.


    Does, in this situation -- see above.


    Cheers, & hth.,

    - Alf

    --
    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?
     
    Alf P. Steinbach, Feb 3, 2008
    #9
  10. * Alf P. Steinbach:
    >
    > Trying to be utterly unambigiously clear:
    >
    > §5.1.3/5


    Huh, who ordered /that/? Of course it should be §8.5.3/5, as I
    mentioned several times already. There's a gremlin in my keyboard.


    Cheers,

    - Alf

    --
    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?
     
    Alf P. Steinbach, Feb 3, 2008
    #10
  11. Niels Dekker - no return address

    James Kanze Guest

    On Feb 3, 8:08 pm, "Alf P. Steinbach" <> wrote:
    > * James Kanze:
    > > On Feb 3, 3:55 pm, "Alf P. Steinbach" <> wrote:
    > >> * James Kanze:
    > >>> On Feb 2, 10:37 pm, "Alf P. Steinbach" <> wrote:
    > >>>> * Niels Dekker - no return address:


    > >>>>> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
    > >>>>> about binding a temporary object to a reference-to-const. Now if this
    > >>>>> temporary isn't const, is it safe to const_cast the reference?


    > >>>>> #include <string>
    > >>>>> #include <cassert>
    > >>>>> using std::string;


    > >>>>> string f() { return "abc"; }


    > >>>>> void h() {
    > >>>>> const string& s1 = f();
    > >>>>> const string& s2 = s1;
    > >>>>> const_cast<string&>(s1) = "new value!"; // Safe?


    > >>>> Not in standard C++, although it might be safe with a
    > >>>> particular compiler.


    > >>> This has always been my believe as well, and I'm almost sure
    > >>> that I once read somewhere in the standard something to the
    > >>> effect that any attempt to modify a temporary (e.g. by such
    > >>> tricks) was undefined behavior. The last time I looked for it,
    > >>> however, I couldn't find it. So could you tell me where in the
    > >>> standard this is forbidden.


    > >> Yes. With respect to the 1998 standard there are two rules
    > >> involved: first, a rule saying that modifying a (we must
    > >> presume "original" is meant) const object incurs Undefined
    > >> Behavior, §7.1.5.1/4, and second, a rule that that the
    > >> reference can be, at the implementation's discretion, a
    > >> reference to an original const object (a new const temporary
    > >> that is copy constructed from the initializer), §8.5.3/5.


    > > I don't see where "orignal" is relevant. This rule concerns the
    > > object itself, and not the type of expression used to refer to
    > > it. In the sample code above, none of the objects are const.
    > > That is, IMHO, the crux of the problem.


    > No, the reference's type -- its cv-qualification -- is very
    > relevant because the cv-qualification can be transferred to a
    > temporary created for the purpose, and no, it's not the case
    > that one is guaranteed that none of the objects are const, in
    > the code above.


    > Trying to be utterly unambigiously clear:


    > §5.1.3/5
    > "A reference to type "cv1 T1" is initialized by an expression of
    > type "cv2 T2" as follows:


    > ...


    > - Otherwise, the reference shall be to a non-volatile const type
    > (i.e. cv1 shall be const)
    > [Example: ...]


    > - If the initializer expression is an rvalue, with T2 a class
    > type, and "cv1 T1" is reference-compatible with "cv2 T2", the
    > reference is bound in one of the following ways (the choice is
    > implementation-defined):


    > - The reference is bound to the object represented by the rvalue
    > (see 3.10) or to a sub-object within that object.


    > - A temporary of type "cv1 T2" [sic] is created, and a constructor
    > is called to copy the entire rvalue into the temporary. The
    > reference is bound to the temporary or to sub-object within
    > the temporary.


    > The last alternative above


    Which is a point I'd definitely overlooked. Of course, this
    alternative has been dropped from the latest draft.

    > is the one that, together with UB for modification of const,
    > dictates UB for the example code. And what happens in this
    > case is that "cv1" is "const", from the declaration of the
    > /reference/ s1, "T2" is "string", from the declaration of f(),
    > and the type of the temporary created is then "const string"
    > -- which is not a typo, but intentional ([sic] means
    > essentially that "this is /not/ a typo"). This is then what
    > the reference is bound to when this case is chosen, namely a
    > new temporary of type "const string".


    Yep. That one character (a 1 instead of a 2) makes a world of
    difference here.

    > And the pure possibility of that alternative being chosen, and
    > that alternative implying UB, means that formally the code is
    > UB.


    Agreed. (That's another thing I'm 100% sure of, but don't know
    off hand where to find it in the standard. But of course,
    nothing else would make sense.)

    I wonder if the committee realizes that by dropping this
    alternative, they're suddenly making undefined behavior defined.

    > > (And I very definitely
    > > remember reading somewhere that attempting to modify an
    > > rvalue---or maybe it was only an rvalue of non-class type---was
    > > undefined behavior, regardless of the const.)


    > For rvalue of non-class type yes I think so, but for rvalue of
    > class type, no, quite the opposite.


    > But I've done enough standardeeze for today, I think. ;-)


    I'll admit that in this case, it's rather sterile. Because even
    if all this were perfectly defined, I still wouldn't want to see
    it in actual code.

    I think, however, that I'll raise the question on the reflectors
    (since comp.std.c++ seems rather dead at the moment). I
    certainly wouldn't mind seeing a phrase added to the effect that
    any attempt to modify an rvalue bound to a reference is
    undefined behavior. (Reminds me too much of some early
    Fortrans, where f(0) might actually call f() with 1 as an
    argument, if a previous call to f() had modified the value.)

    --
    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 4, 2008
    #11
  12. James Kanze wrote:
    > I think, however, that I'll raise the question on the reflectors
    > (since comp.std.c++ seems rather dead at the moment). I
    > certainly wouldn't mind seeing a phrase added to the effect that
    > any attempt to modify an rvalue bound to a reference is
    > undefined behavior.


    Would that include modifying a *mutable* data member of an rvalue?

    Anyway, I just added a link to this discussion on Herb Sutter's blog:
    http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry
    Thanks for your feedback!

    Niels
     
    Niels Dekker - no return address, Feb 4, 2008
    #12
  13. * Niels Dekker - no return address:
    > James Kanze wrote:
    >> I think, however, that I'll raise the question on the reflectors
    >> (since comp.std.c++ seems rather dead at the moment). I
    >> certainly wouldn't mind seeing a phrase added to the effect that
    >> any attempt to modify an rvalue bound to a reference is
    >> undefined behavior.

    >
    > Would that include modifying a *mutable* data member of an rvalue?


    The question is one of supporting or not optimizations based on the
    compiler knowing that a value can't be modified without UB. So
    presumably the rule should simply be that the object referred to by an
    ordinary reference bound to an rvalue, is regarded as a const object
    (originally const object) with regard to later access. Then modifying a
    mutable member would be OK, and this rule would just reinstate the
    relevant part of the rule that was removed in the C++0x draft.

    Just my 2c,

    - Alf

    --
    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?
     
    Alf P. Steinbach, Feb 4, 2008
    #13
  14. * Niels Dekker - no return address:
    >
    > Anyway, I just added a link to this discussion on Herb Sutter's blog:
    > http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry
    > Thanks for your feedback!


    Niels, could you please add a comment for me (that darned system
    requires some "Windows Live ID" whatever the heck that is):

    Herb's attribution/credit for ScopeGuard is incorrect.

    It was Petru Marginean's child, not Andrei Alexandrescu's.

    Though Andrei helped add another abstraction layer, and co-authored the
    DDJ article (I think it was DDJ, unless it was C/C++ User's Journal).


    Cheers, & a bit frustrated, & TIA.,

    - Alf

    --
    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?
     
    Alf P. Steinbach, Feb 4, 2008
    #14
  15. Niels Dekker - no return address

    Guest

    by the way , I tested what you are dicussing using VC2005 but find
    another problem that confused me. who can help me understand it?

    int a = 1;
    const int& b= 1; // legal //using the mechanism is using const
    reference to temporary.
    //int& c = 1; //illegal // why? but according to what you talked ,
    it is not the rule though, it can
    be supported by VS2005, just like
    string& s1 = f();
    int& d= a; //legeal
    const int& e = a; //legal // why it is legal? const reference also
    can point to non-const content memory?
    int& f = b; //ilegal; from const to non-const error
     
    , Feb 5, 2008
    #15
  16. * :
    > by the way , I tested what you are dicussing using VC2005 but find
    > another problem that confused me. who can help me understand it?
    >
    > int a = 1;
    > const int& b= 1; // legal //using the mechanism is using const
    > reference to temporary.
    > //int& c = 1; //illegal // why? but according to what you talked ,
    > it is not the rule though, it can
    > be supported by VS2005, just like


    I think you mean, why is it disallowed by the standard, when there
    exists at least one compiler that does support it (for class type
    objects) as a language extension, showing by example that it could in
    principle be allowed.

    One example is

    void foo( int& x ) { x = 666; }

    int main()
    {
    foo( 42 );
    }

    One would want this to not compile.

    Another example involves implicit type conversion, where the object
    modified is not the actual argument but a temporary created from the
    actual argument. One would want that also to not compile, instead of
    silently doing nothing. However, the problem could be avoided by
    disallowing automatic creation of a temporary for such arguments.

    The short summary is that disallowing that binding is practically
    useful, whereas allowing it (as it was originally) turned out to create
    a host of problems.

    The Annotated Reference Manual (a.k.a. the "ARM") says of the earlier
    more permissive rule that it was "a major source of errors and surprises".


    > string& s1 = f();


    This is invalid unless f() returns a reference or an object convertible
    to reference.


    > int& d= a; //legeal
    > const int& e = a; //legal // why it is legal? const reference also
    > can point to non-const content memory?


    Yes. It doesn't make that object const in itself. It just restricts
    what you can do with the object via the reference.


    Cheers, & hth.,

    - Alf

    --
    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?
     
    Alf P. Steinbach, Feb 5, 2008
    #16
  17. >> Anyway, I just added a link to this discussion on Herb Sutter's blog:
    >> http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry


    Alf P. Steinbach wrote:
    > Niels, could you please add a comment for me (that darned system
    > requires some "Windows Live ID" whatever the heck that is):
    >
    > Herb's attribution/credit for ScopeGuard is incorrect.
    > It was Petru Marginean's child, not Andrei Alexandrescu's.
    >
    > Though Andrei helped add another abstraction layer, and co-authored the
    > DDJ article (I think it was DDJ, unless it was C/C++ User's Journal).


    I'm sorry for you but it's not such a big deal to me. There's a link to
    the article by Andrei and Petru on Herb's blog entry. And there's a
    link from his page to this newsgroup thread as well, by now. :) So a
    reader of Herb's blog can easily find out that the ScopeGuard is
    originally from Petru...

    If you still think Herb's blog should explicitly mention Petru (which
    would of course be more correct), why don't you just send him an
    e-mail? His mail address is at www.gotw.ca. Otherwise just get
    yourself that "Windows Live ID", whatever the heck. You can get it for
    free, at least if you're willing to give some of your personal
    information to Microsoft...

    Hope that helps,

    Thanks again for your feedback,

    --
    Niels Dekker
    http://www.xs4all.nl/~nd/dekkerware
    Scientific programmer at LKEB, Leiden University Medical Center
     
    Niels Dekker - no return address, Feb 5, 2008
    #17
  18. Alf P. Steinbach wrote:
    > Herb's attribution/credit for ScopeGuard is incorrect.
    > It was Petru Marginean's child, not Andrei Alexandrescu's.


    He just fixed it :) Cool!
    http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry

    -- Niels
     
    Niels Dekker - no return address, Feb 6, 2008
    #18
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Srini
    Replies:
    5
    Views:
    320
    Herb Sutter
    Sep 10, 2005
  2. George2

    Challenging GotW 66's moral

    George2, Dec 27, 2007, in forum: C++
    Replies:
    4
    Views:
    413
    Salt_Peter
    Dec 27, 2007
  3. Replies:
    7
    Views:
    3,331
    James Kanze
    Feb 12, 2008
  4. puzzlecracker

    stock unwinding from GotW #47

    puzzlecracker, May 1, 2008, in forum: C++
    Replies:
    0
    Views:
    335
    puzzlecracker
    May 1, 2008
  5. Gabriel Rossetti
    Replies:
    0
    Views:
    1,396
    Gabriel Rossetti
    Aug 29, 2008
Loading...

Share This Page