Topic: Reference initialization unnecessarily restrictive.
Author: "Dave Bridger" <dbridger@inlink.com>
Date: 1998/02/19 Raw View
In section 8.5.3, paragraph 5, of the C++ Draft Standard dealing with reference
initialization, it explicitly requires that when:
"A reference to type "cv1 T1" is initialized by an expression of type "cv2
T2"
the initializer expression must be an lvalue. Yet paragraph 6 allows
initialization if:
"T2 is a class type, and the initializer expression can be
implicitly converted to an lvalue of type "cv3 T3," where "cv1 T1" is
reference-compatible with "cv3 T3"
It appears to me that the requirement that the intializer expression must
always be an lvalue is unnecessarily restrictive.
In both the ARM and in D&E, Stroustrup is adamantly opposed to allowing
initialization with non-lvalues and gives strong reasons. However, I believe
that this restriction remains from the days before the language in paragraph 6
was added. I believe that the requirement that T2 be a class and that there be
available a conversion to an acceptable lvalue is sufficient to guard against
the problems about which Stroustrup is concerned.
The explicit prohibition of non-lvalues interferes with the use of a technique
which, ironically, Stroustrup illustrates in D&E immediately after explaining
that it was a "serious mistake" to allow "a non-const reference to be
initialized by a non-lvalue." He gives an example of a helper class for a
String class which preserves the necessary information so that the proper
determination between lvalue and rvalue usage of an overloaded operator[] can
be made.
Stroustrup's String Example (D&E page 88):
class char_ref { // identify a character in a String
friend class String;
int i;
String* s;
char_ref(String* ss, int ii) { s=ss; i = ii; }
public:
void operator=(char c);
operator char();
};
class String {
friend class char_ref;
char* r;
public:
char_ref operator[](int i)
{ return char_ref(this,i); }
// ...
};
void char_ref::operator=(char c) {s->r[i]=c; }
char_ref::operator char() { return s->r[i]; }
However, the technique illustrated in this example will fail if an attempt is
made to call a function with an actual argument that utilizes the operator[].
void func(char&); // function that stores a character.
...
String a;
func(a[1]); // Will fail.
The function call will fail because the char_ref object is not a proper
initializer for the char& argument of the function. One would think that the
problem could be solved by merely adding a function to the class char_ref that
produces a conversion to char&.
char_ref::operator char&() { return s->r[i]; } // Conversion to lvalue.
Unfortunately, this will fail also, even though it meets the requirements of
paragraph 6. It fails simply because the temporary object "char_ref" is not an
lvalue even though an acceptable conversion to an lvalue is provided.
It would be unfortunate if the temporary object technique were to remain
hobbled when used in function calls merely due to a restriction which is no
longer needed in quite such broad terms.
--Dave Bridger
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html ]