Re: GotW #88: Is it safe to const_cast a reference to a temporary?
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 th=
is
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, =A77.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), =A78.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:
=A75.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:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34