Topic: Problem with resolution to Issue 453
Author: johnchx2@yahoo.com
Date: 20 Oct 2005 02:30:01 GMT Raw View
Currently, a reference must be initialized to refer to a "valid" object
(8.3.2/4). There is some dispute about whether "valid" means "live"
(where live = "after the object's lifetime has begun and before the
object's lifetime has ended"). In other words, it is not clear whether
initializing a reference to refer to uninitialized storage results in
undefined behavior.
The resolution to Issue #363 implies that a reference MAY be
initialized to designate uninitialized storage:
http://www2.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#363
However, this resolution appears to be based on a mis-reading of 3.8/6.
While 3.8/6 permits the use of lvalues which designate "non-live"
objects (i.e. uninitialized and reused storage), nothing in that
paragraph explicitly authorizes binding a reference to such an lvalue.
It *does* say that "using the properties of the lvalue which do not
depend on its value is well-defined." However, for at least some
plausible definitions of "valid object," validity does depend upon the
object's value. (For example, it would seem strange to claim that an
object was a "valid int" if loading its bit-pattern into an
"int-appropriate" register caused a hardware trap.)
The proposed resolution to Issue 453 would eliminate any ambiguity on
this score, making it legal to bind a reference to an uninitialized
region of storage (with size and alignment corresponding to the type of
the reference).
http://www2.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#453
If adopted, this resolution would make it effectively impossible to
write functions taking reference parameters which do not exhibit
undefined behavior. This is because the resolution transfers the
responsibility for "knowing" that a particular lvalue designates
uninitialized storage -- and treating it accordingly -- from the caller
(which CAN know this) to the callee (which, in general, CANNOT tell the
difference).
To avoid undefined behavior, functions taking reference parameters must
treat such references as if they *might* refer to raw storage, meaning
that only their type, size and address may be used. Such functions
must also assume that reference parameters *might* refer to live
objects, so they can't re-use the designated storage if the designated
object's type has a non-trivial destructor. (You can't re-use the
storage without calling the dtor, and you can't call the dtor because
the object might be nothing but raw storage.)
Certain types defined by the standard library will become impossible to
implement as now specified, if the resolution is adopted. For example,
the "two-parameter" constructor of std::pair does not specify as a
pre-condition that the source objects must be "alive." It is therefore
legal to pass raw storage to the constructor, and a conforming
implementation must not fail. This is clearly impossible.
Pre-conditions may therefore need to be added to the definitions of
*all* standard library functions (including member functions) which
take reference parameters.
The parallel problem very likely permeates user code. A reference
parameter in a function signature is a universally recognized
self-documenting (and, technically, self-enforcing) requirement that
the function may only be called with a "live" object. Removing the
basis for that expectation from the language could be viewed as
somewhat disruptive.
The benefit of allowing references to be bound to raw storage appear
negligible. The use case mentioned in both issue 363 and 453 involves
passing a constructor a reference to the object being constructed, to
allow overloading the constructor on the const-ness of the object being
constructed. But can't this can be accomplished equally well using a
pointer instead of a reference?
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]