Topic: rvalue.non_const_member_function() (WAS const return value...)
Author: mat@mole-end
Date: Sat, 4 Jul 1992 22:53:11 GMT Raw View
In article <KENDALL.92Jul1015527@pen.centerline.com>, kendall@centerline.com (Sam Kendall) writes:
> I am convinced that a const return value does not make sense, and
> although there is nothing in the ARM or in the current ANSI WP (working
> paper, 92-0060) that prohibits them or normalizes away the const, there
> should be. (This is about functions declared to return a "bare" const
> type. Returning a reference or a pointer to const is fine.)
>
> The basic reason is that const is an attribute of variables, of
> lvalues, and of types that are pointed to or referenced. It is NOT an
> attribute of rvalues. An rvalue is already unchangeable -- making it
> const does not alter the set of operations allowed on it. And the
> return value of a function is an rvalue. For example:
Not entirely true; it can affect overloading. For example:
class Ksorklkky
{
public:
void ginggyfunc( int );
void ginggyfunc( int ) const;
. . .
};
const Ksorklkky grommyfunc( const char* );
. . .
grommyfunc( "qskqwygynee" ).ginggyfunc( 0 ); // Calls the const
// version ...
--
(This man's opinions are his own.)
From mole-end Mark Terribile
uunet!mole-end!mat, Somewhere in Matawan, NJ
Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Sun, 5 Jul 1992 11:46:44 GMT Raw View
In article <1992Jul4.225311.4124@mole-end> mat@mole-end writes:
>In article <KENDALL.92Jul1015527@pen.centerline.com>, kendall@centerline.com (Sam Kendall) writes:
>> I am convinced that a const return value does not make sense, and
>> although there is nothing in the ARM or in the current ANSI WP (working
>> paper, 92-0060) that prohibits them or normalizes away the const, there
>> should be. (This is about functions declared to return a "bare" const
>> type. Returning a reference or a pointer to const is fine.)
>>
>> The basic reason is that const is an attribute of variables, of
>> lvalues, and of types that are pointed to or referenced. It is NOT an
>> attribute of rvalues. An rvalue is already unchangeable -- making it
>> const does not alter the set of operations allowed on it. And the
>> return value of a function is an rvalue. For example:
>
What is an rvalue/lvalue? The concepts do not make much
sense given user assignment.
An the return value of a function might be an lvalue!
int k;
int & f(){ return k;}
f()=2;
I am beginning to think a NON-const return value does not make sense.
That is because to apply a member function to an object requires
a reference (or pointer?). For a temporary value to be converted
to a reference it must be const. Non-const references may not
be bound to temporaries.
Another point of view is simply that the 'const' does not affect
the type of a value. A value is neither const nor non-const,
it is the NAME that is const or non-const.
Thus in the example
const int f(const int);
BOTH uses of 'const' are wrong. [const parameters in declarations---
as opposed to definitions---are meaningless]
You will note that the type of '5' is int, and not const int.
--
;----------------------------------------------------------------------
JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
Author: kendall@centerline.com (Sam Kendall)
Date: Wed, 1 Jul 1992 06:55:26 GMT Raw View
I am convinced that a const return value does not make sense, and
although there is nothing in the ARM or in the current ANSI WP (working
paper, 92-0060) that prohibits them or normalizes away the const, there
should be. (This is about functions declared to return a "bare" const
type. Returning a reference or a pointer to const is fine.)
The basic reason is that const is an attribute of variables, of
lvalues, and of types that are pointed to or referenced. It is NOT an
attribute of rvalues. An rvalue is already unchangeable -- making it
const does not alter the set of operations allowed on it. And the
return value of a function is an rvalue. For example:
int f();
const int g();
f() = 5; // illegal
g() = 6; // illegal as well
The next question is, are return values of class type any different
in this respect than return values of non-class type? The answer is no;
a function that returns class type is still returning an rvalue, not an
lvalue. The fact that implementations use a temporary to hold the
return value is not relevant. (Look out, thin ice!)
What is confusing about rvalues is that the trivial conversion T-->T&
(section 13.2, ARM p. 318, WP paragraph 11) can transform them into
lvalues. However, this conversion is prohibited from generating a
temporary if the result is bound to a reference to non-const. Here is
an example from an earlier posting by John (MAX) Skaller; it works in
all compilers I've tried including cfront 3.0, but it is illegal:
struct S {
void mf ();
void cmf () const;
};
extern S f();
void foo() {
f().mf(); // succeeds in many compilers; SHOULD BE ILLEGAL
S& r = f(); // essentially the same case, but fails
f().cmf(); // legal
const S& cr = f(); // essentially the same case, also legal
}
Why do I think the call `f().mf()' is illegal? `f()' returns an rvalue
of type S. The argument matching for `mf' treats `f()', the left-hand
side of the dot operator, as a special extra argument (section 13.2, ARM
p. 316, 92-0060 paragraph 4).
Now the argument continues slightly differently between the ARM and
the WP. ARM: "No temporaries will be introduced for this extra
argument" (13.2 p. 316), but a temporary must be introduced to allow the
extra formal argument, which is of type S&, to be initialized. WP: "a
temporary may not be used to initialize a non-const reference" (13.2
paragraph 9). Note that even if the IMPLEMENTATION reuses another
temporary rather than generating a new one, in the ABSTRACT LANGUAGE a
temporary is generated at this point. (The ice is very thin here!)
In summary, a temporary must be generated, but it is illegal to bind
a temporary to the reference-typed "extra argument" of the member
function call. So the call is not legal.
Here's an appeal to intuition on the same point. Consider this
member function call:
class A {
int i;
public:
void set_i(int); // sets i to a value
};
extern A f2();
void f() {
f2().set_i(5); // dangerous!
}
This example is essentially the same as `f().mf()' (in the earlier
example). But this new example is dangerous -- the side effect is
thrown away, but the programmer is not told about this.
In conclusion, const return values, and const in other rvalue
contexts, should be prohibited or normalized away. Also, member
function calls of the form
rvalue . non_const_member_function()
are both illegal and undesirable.
I am interested to hear a rebuttal, either about the legality of
these member function calls or about their desirability. It's so late
at night that I'm probably off in deep space. Congratulations if you've
managed to read this far.
----
Sam Kendall
CenterLine Software, Inc. (formerly Saber Software)
Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Wed, 1 Jul 1992 18:42:14 GMT Raw View
In article <KENDALL.92Jul1015527@pen.centerline.com> kendall@centerline.com (Sam Kendall) writes:
>
> I am convinced that a const return value does not make sense, and
>although there is nothing in the ARM or in the current ANSI WP (working
>paper, 92-0060) that prohibits them or normalizes away the const, there
>should be. (This is about functions declared to return a "bare" const
>type. Returning a reference or a pointer to const is fine.)
>
> The basic reason is that const is an attribute of variables, of
>lvalues, and of types that are pointed to or referenced. It is NOT an
>attribute of rvalues. An rvalue is already unchangeable -- making it
>const does not alter the set of operations allowed on it.
This not true at the moment for user defined objects.
It is possible to assign to an rvalue of class type.
You might think that is worthless, but if the assignment
(or other mutator) has side effects that is not true.
The case that started the discussion is the implementation
of the substring operation. It is commonly done by
s(1,2)="Fred";
where s(1,2) returns a temporary object of type Substring (containing
a pointer to 's')
>And the
>return value of a function is an rvalue. For example:
>
> int f();
> const int g();
> f() = 5; // illegal
> g() = 6; // illegal as well
>
> The next question is, are return values of class type any different
>in this respect than return values of non-class type? The answer is no;
>a function that returns class type is still returning an rvalue, not an
>lvalue.
The notion of 'lvalue' and 'rvalue' breaks down for
user defined types. Saying a function return value is an rvalue
then says nothing. What is an rvalue is defined by the ACTUAL
language rules (implicitly).
In the case of returning a reference, it is an lvalue
unless const reference, then rvalue. For a SIMPLE object,
the idea of modifying an lvalue is just meaningless, but if
the object is complicated (constains references or pointers)
then side effects can be used to make modifying an rvalue
meaningful.
>The fact that implementations use a temporary to hold the
>return value is not relevant. (Look out, thin ice!)
Yes, it is. If the USER put the return value in
a variable, they would have explicit control over whether
that variable was const or not.
For temporaries, it is not the same (at the moment)
because it is not defined!
>
> What is confusing about rvalues is that the trivial conversion T-->T&
>(section 13.2, ARM p. 318, WP paragraph 11) can transform them into
>lvalues. However, this conversion is prohibited from generating a
>temporary if the result is bound to a reference to non-const. Here is
>an example from an earlier posting by John (MAX) Skaller; it works in
>all compilers I've tried including cfront 3.0, but it is illegal:
>
> struct S {
> void mf ();
> void cmf () const;
> };
> extern S f();
> void foo() {
> f().mf(); // succeeds in many compilers; SHOULD BE ILLEGAL
> S& r = f(); // essentially the same case, but fails
>
> f().cmf(); // legal
> const S& cr = f(); // essentially the same case, also legal
> }
>
>Why do I think the call `f().mf()' is illegal? `f()' returns an rvalue
>of type S. The argument matching for `mf' treats `f()', the left-hand
>side of the dot operator, as a special extra argument (section 13.2, ARM
>p. 316, 92-0060 paragraph 4).
>
> Now the argument continues slightly differently between the ARM and
>the WP. ARM: "No temporaries will be introduced for this extra
>argument" (13.2 p. 316), but a temporary must be introduced to allow the
>extra formal argument, which is of type S&, to be initialized. WP: "a
>temporary may not be used to initialize a non-const reference" (13.2
>paragraph 9). Note that even if the IMPLEMENTATION reuses another
>temporary rather than generating a new one, in the ABSTRACT LANGUAGE a
>temporary is generated at this point. (The ice is very thin here!)
>
> In summary, a temporary must be generated, but it is illegal to bind
>a temporary to the reference-typed "extra argument" of the member
>function call. So the call is not legal.
>
> Here's an appeal to intuition on the same point. Consider this
>member function call:
>
> class A {
> int i;
> public:
> void set_i(int); // sets i to a value
> };
> extern A f2();
> void f() {
> f2().set_i(5); // dangerous!
> }
>
>This example is essentially the same as `f().mf()' (in the earlier
>example). But this new example is dangerous -- the side effect is
>thrown away, but the programmer is not told about this.
But in the 'string'/'substring' case, the side effect
is not thrown away, it is precisely what we want.
And anyhow, with 'i=j'; the return value is *also* thrown
away!
>
> In conclusion, const return values, and const in other rvalue
>contexts, should be prohibited or normalized away. Also, member
>function calls of the form
>
> rvalue . non_const_member_function()
>
>are both illegal and undesirable.
>
> I am interested to hear a rebuttal, either about the legality of
>these member function calls or about their desirability. It's so late
>at night that I'm probably off in deep space. Congratulations if you've
>managed to read this far.
>
>----
>Sam Kendall
>CenterLine Software, Inc. (formerly Saber Software)
To do string/substring we then need TWO substring classes,
namely, constSubstring and Substring. The same applies to ANY handle
class I think. It is messy. [That is not a rebuttal. There is no
issue about desirability I think, the issue is whether it makes any
sense at all.]
I will have to check this, but I think it is NECESSARY
to know whether const return types exist or not. Knowing
either way is OK, but not knowing makes it IMPOSSIBLE to
write any code at all relying on applying a function to a temporary.
If const and non-const returns are distinguished,
one can select one of two functions
f() const;
f();
to use on the object.
If returns are always const, only f() const will be selected,
and if returns are always non-const, only f() will be selected.
If it is implementation dependent or undefined,
HOW DO YOU KNOW WHICH FUNCTIONS TO WRITE?
Problem: implement
s(1,2)
for strings so that it returns a string value if an rvalue,
but you can do
s(1,2)="Fred";
if, and only if, s is non-const.
In your scheme (which seems right) you MUST have TWO classes
constSubString
SubString
but perhaps that is right.
--
;----------------------------------------------------------------------
JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------