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. :-(
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.
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.
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.
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.
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.)