On Feb 3, 8:08 pm, "Alf P. Steinbach" <al...@start.no> wrote:
> * James Kanze:
> > On Feb 3, 3:55 pm, "Alf P. Steinbach" <al...@start.no> wrote:
> >> * James Kanze:
> >>> On Feb 2, 10:37 pm, "Alf P. Steinbach" <al...@start.no> 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