[comp.lang.c++] const

walsh@dude.epps.kodak.com (Joe Walsh) (08/25/90)

I have a question about the use of const in member functions. It seems to me taht there are three different uses for const in member functions. They are:

1) constant arguments to the function A::f(const x)
2) constant return type from the function const char* A::g()
3) constant function where the object or 'this' is a constant.

The second and third types seems to confuse me. Are there really two distinct types, and what is the syntax.

I have seen 

int A::f() const; in some code as well as int const A::f(); and I am not sure if there is a diference. The former syntax does not seem to be accepted by g++.

any help would be appreciated, thanks

--
Joe Walsh                                            Atex Publishing Systems
165 Lexington Road, 400/165L                              Billerica MA 01821
Internet: <walsh@epps.kodak.com>                Usenet: <walsh@atexnet.uucp>
Voice: 508-670-4022                                        Fax: 508-670-4033

sarima@tdatirv.UUCP (Stanley Friesen) (08/29/90)

[I tried to mail this, but it bounced, so here it is]

In article <5027@atexnet.UUCP> you write:
>I have a question about the use of const in member functions. It seems to me taht there are three different uses for const in member functions. They are:
>
>1) constant arguments to the function A::f(const x)
>2) constant return type from the function const char* A::g()
>3) constant function where the object or 'this' is a constant.
>
>The second and third types seems to confuse me. Are there really two distinct
>types, and what is the syntax.

Yes, and in fact #3 is more closely related to #1 than #2 (since the object
is passed as hidden extra argument in most implementations).

Case #2 is used where the function returns some private data that should not
be modified by the caller.

Case #3 is used where the member function should not be allowed to modify the
state of the object it is invoked on.
>
>I have seen 
>
>int A::f() const; in some code as well as int const A::f(); and I am not sure
>if there is a diference. The former syntax does not seem to be accepted by g++.

	You seem to have an old version of g++.  The former syntax is new to
cfront version 2.0, and its equivalents from other vendors. (The latest g++
does in fact accept it, I believe).

int A::f() const;   declares a 'constant function' where 'this' is constant.
int const A::f();   is the same as:
const int A::f();   and declares a function returning a constant result,
		    which is only meaningful if the function returns a pointer
		    or a reference.

vaughan@puma.cad.mcc.com (Paul Vaughan) (09/05/90)

Awhile back I posted a message asking if const was really worth the
trouble.  Everyone who replied either on the net or privately thought
that making a program const correct was indeed worth the effort.
These were all pretty subjective opinions--it's kind of hard to
measure how much difference it makes.

So, here's a const question.

Consider the following function


const char* substring(const char* string, const char* sub) 
// substring searches for the first occurence of sub in string and
//returns a pointer to it or 0 if none is found.
{ ....}

If I'm working on a const char* (and, therefore, don't plan on changing
any part of it) this works just fine.  However, if I'm working on a
char* and was planning to change part of it and was using substring to
find that part, this will cause a problem because substring returns a
const.  For example,

char* foo = "barfoobar";
*substring(foo,"foo") = `\0`;

will give an error due to the modification of a non-const return value.


I can't declare substring as 

char* substring(const char* string, const char* sub) 

because it is returning a pointer to part of the string that is passed
in.  I would declare it

char* substring(char* string, const char* sub)

but then I can't use it to deal with const char*'s, and, of course, I can't
overload it based on the return type!


So what do I do?  My solution so far has been to declare it 

const char* substring(const char* string, const char* sub)

and when I want to work on a char*, I do a cast, like this

char* foo;
char* bar = (char*) substring(foo, "foo");

Given the semantics of the function and that I'm passing in a char*, I
can verify the safety of the cast without much trouble.  Is there a
better way?


 Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
 Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan

sarima@tdatirv.UUCP (Stanley Friesen) (09/05/90)

In article <10863@cadillac.CAD.MCC.COM> vaughan@puma.cad.mcc.com (Paul Vaughan) writes:
>Consider the following function
 
>const char* substring(const char* string, const char* sub) 
>// substring searches for the first occurence of sub in string and
>//returns a pointer to it or 0 if none is found.
>{ ....}
>
>If I'm working on a const char* (and, therefore, don't plan on changing
>any part of it) this works just fine.  However, if I'm working on a
>char* and was planning to change part of it and was using substring to
>find that part, this will cause a problem because substring returns a
>const. ...
 
>I can't declare substring as 
 
>char* substring(const char* string, const char* sub) 
 
>because it is returning a pointer to part of the string that is passed
>in.  I would declare it
>
>char* substring(char* string, const char* sub)
>
>but then I can't use it to deal with const char*'s, and, of course, I can't
>overload it based on the return type!

But I believe you *can* overload it on the type of the first argument based on
the presence/absence of 'const'.  In other words, declare it *both* the first
and the second way.  This will then call either the const or non-const version
depending on whether the first argument is a const string or not.
---------------------------------
uunet!tdatirv!sarima				(Stanley Friesen)
-L
-I
-N
-E
-
-C
-N
-T

twagner@baobab.berkeley.edu (Tim Wagner) (09/06/90)

In article <10863@cadillac.CAD.MCC.COM>, vaughan@puma.cad.mcc.com (Paul
Vaughan) writes:
|> 
|> Awhile back I posted a message asking if const was really worth the
|> trouble.  Everyone who replied either on the net or privately thought
|> that making a program const correct was indeed worth the effort.
|> These were all pretty subjective opinions--it's kind of hard to
|> measure how much difference it makes.
|> 
|> So, here's a const question.
|> [deleted]

This points out once again the problems inherent in C's (and therefore C++'s)
use of 'const'.  The return type of the function is not a good mechanism
in this case: obviously, one cannot really alter the returned value anyhow,
so calling it 'const' is redundant.  However, the context in which that
returned value is used should be controlled; that is, an lvalue getting
such a returned object should be const itself.  That brings us to the
problem of "really constant" versus "sometimes contstant" which has already
been beaten to death (by me and others--see back issues for more).

In other words, you're right; it's ugly and broken.

Tim Wagner
twagner@sequoia.Berkeley.EDU

ark@alice.UUCP (Andrew Koenig) (09/07/90)

In article <27503@pasteur.Berkeley.EDU>, twagner@baobab.berkeley.edu (Tim Wagner) writes:

> This points out once again the problems inherent in C's (and therefore C++'s)
> use of 'const'.  The return type of the function is not a good mechanism
> in this case: obviously, one cannot really alter the returned value anyhow,
> so calling it 'const' is redundant.  However, the context in which that
> returned value is used should be controlled; that is, an lvalue getting
> such a returned object should be const itself.

Not at all!  If you assign the result of a function to a variable,
the variable gets a copy of that result.  Just because the result
itself is a constant, that doesn't say anything about the copy.

For example:

	int x = 3;

Here, 3 is a `const int,' but that surely does not oblige x to be
a const!   Similarly:

	extern const int f();

	main()
	{
		int x = f();
		// ...
	}

Again, there is no particular reason why x should have to be a const.
-- 
				--Andrew Koenig
				  ark@europa.att.com

davidm@uunet.UU.NET (David S. Masterson) (09/07/90)

In article <142@tdatirv.UUCP> sarima@tdatirv.UUCP (Stanley Friesen) writes:

   But I believe you *can* overload it on the type of the first argument based
   on the presence/absence of 'const'.  In other words, declare it *both* the
   first and the second way.  This will then call either the const or
   non-const version depending on whether the first argument is a const string
   or not.

The point is that:

	const char* substring(const char*, ...)
		// first argument is important for this discussion

has a very unuseful return value (because its a constant).  Changing it to:

	char* substring(char*, ...)

makes the return value useful again, but removes the hint about what might be
optimized by the compiler (like whether its valid to place the arguments or
returns in registers).  Note also that for the substring() function:

	char* substring(const char*, ...)

is not possible (in this argument) because of the definition that "once a
const, always a const" (the return value will be a pointer to something in the
argument, therefore, its a const char*).  Because of that definition,
overloading these last two is not possible.

The point is that in this example, you would like to use CONST on the argument
in order to make it plain that the function will not be changing the argument
(which provides optimization information for the function).  However, you
don't want to use CONST on the return value because it is likely that the user
of the function will want to change the substring once substring() found it.
But, by definition of "once a const, always a const", this does not seem to be
possible.
--
====================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"

ark@alice.UUCP (Andrew Koenig) (09/07/90)

In article <CIMSHOP!DAVIDM.90Sep6181221@uunet.UU.NET>, cimshop!davidm@uunet.UU.NET (David S. Masterson) writes:

> The point is that:

> 	const char* substring(const char*, ...)
> 		// first argument is important for this discussion

> has a very unuseful return value (because its a constant).

But if substring returns a pointer into the first argument, then the
return value had better be const!
>							      Changing it to:

> 	char* substring(char*, ...)

> makes the return value useful again, but removes the hint about what might be
> optimized by the compiler (like whether its valid to place the arguments or
> returns in registers).  Note also that for the substring() function:

> 	char* substring(const char*, ...)

> is not possible (in this argument) because of the definition that "once a
> const, always a const" (the return value will be a pointer to something in the
> argument, therefore, its a const char*).  Because of that definition,
> overloading these last two is not possible.

But the following is most certainly possible:

	const char* substring(const char*, const char*);
	char* substring(char*, const char*);

and as far as I can see, it will do exactly the right thing.

> The point is that in this example, you would like to use CONST on the argument
> in order to make it plain that the function will not be changing the argument
> (which provides optimization information for the function).  However, you
> don't want to use CONST on the return value because it is likely that the user
> of the function will want to change the substring once substring() found it.

If the argument is memory that can't be changed, and the return value
is part of the argument, then the return valud had better be immutable
as well.

I should point out, though, that if you have a pointer to mutable
memory and you convert that pointer to a pointer to const, it is
entirely legitimate to cast it back.  Therefore, there's no reason not
to write the following:

	extern const char* substring(const char*, const char*);
	inline char* substring(char* p, const char* q)
	{
		return (char*) substring((const char*) p, q);
	}

At first glance, one might think it unnecessary to cast p to
const char*, but one would be wrong: without the cast, the
result would be a recursion loop.  Think about it.
-- 
				--Andrew Koenig
				  ark@europa.att.com

mat@mole-end.UUCP (Mark A Terribile) (09/08/90)

In article <10863@cadillac.CAD.MCC.COM>, vaughan@puma.cad.mcc.com (Paul Vaughan) writes:
 . . .
> So, here's a const question.
 . . .
> Consider the following function
  . . .
> const char* substring(const char* string, const char* sub) 
> // substring searches for the first occurence of sub in string and
> //returns a pointer to it or 0 if none is found.
. . .
> [But what if I want to operate on non-const characters??]

Yup, you've got a real problem, though not an insurmountable one.

Starting with Release 2.0, you should be able to overload on
const/non-const-ness .  You can't do it on the basis of the return
type, but you can do it on the basis of the first string.

	const char* substring( const char* string, const char* sub );
	char* substring( char* string, const char* sub );

Does this mean that you have to implement it twice (ignoring that the first
substring() may already exist elsewheres)?  Weeeeeelllll ...

I don't think it unreasonable to write

	inline char*
	substring( char* string, const char* sub )
	{
		return (char*) substring( (const char*) string, sub );
	}

There are some places where this sort of thing won't work; you really need
different algorithms.

It would be nice to be able to say that a function's return type is const
if certain actual arguments are const.  This is probably not something we
will see soon, except perhaps in templates.  Unfortunately, when you use
templates, it is expected (last time I checked) that a weaker form of
argument matching will be applied than for ordinary functions.
-- 

 (This man's opinions are his own.)
 From mole-end				Mark Terribile

davidm@uunet.UU.NET (David S. Masterson) (09/10/90)

In article <11306@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
>
> If the argument is memory that can't be changed, and the return value
> is part of the argument, then the return value had better be immutable
> as well.
>
> I should point out, though, that if you have a pointer to mutable
> memory and you convert that pointer to a pointer to const, it is
> entirely legitimate to cast it back.  

Exactly the point of the substring() example.  I guess the question here is
can the compiler know and keep track of the differences between a pointer to
mutable memory and a pointer to immutable memory if both are declared as
"const char*"?

>					Therefore, there's no reason not
> to write the following:
>
>	   extern const char* substring(const char*, const char*);
>	   inline char* substring(char* p, const char* q) {
>		   return (char*) substring((const char*) p, q);
>	   }
>
> At first glance, one might think it unnecessary to cast p to
> const char*, but one would be wrong: without the cast, the
> result would be a recursion loop.  Think about it.

Obviously, the cast of p is necessary to prevent recursion, but isn't the cast
of the return value of substring() a violation of the idea that "once a const,
always a const" (which, I thought, was the point of the discussion)?  How can
the compiler know that the result of:

	extern const char* substring(const char*, const char*);

is mutable because it is a direct result of the p (which is mutable, but
casted to const)?  It would seem that this could only be known in a separate
compilation step.  As far as this compilation step is concerned, this
substring could work in a couple of ways -- return a pointer into the first
argument or copy the first argument to someplace truly read-only and return a
pointer to that.  The compiler can't know, can it?
--
====================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mtn. View, CA  94043
====================================================================
"If someone thinks they know what I said, then I didn't say it!"