Topic: const function & function prototype: answers


Author: hstof@nki.nl
Date: 7 Nov 91 16:09:27 GMT
Raw View
In article <1991Nov5.163825.7273@nki.nl>, I asked whether it is legal C++
to have a const as well as a "non-const" member function of the same
(overloaded) name?
I have received a number of widely differing answers. Apparantly my question
touched upon a topic that is rather ill-defined in the literature. It is clear
that different return types are not sufficient to distinguish member functions
of the same name: different member functions of the same class with identical
names must have distinct signatures. It is also stated clearly in Ellis &
Stroustrup that variation in type and number of function arguments is a way
of creating distinct signatures, and that "const" may indeed be used to
specify distinct argument type. The unclarity boils down to the following
question:
        Is the const specifier, as applied to a member function rather than
        a function argument, also considered part of the member function's
        signature, or is not?

Of the responses that I have received sofar, the majority expresses the opinion
that it is indeed legal C++ to have a class that has a const and a non-const
function of the same name, as in the example that I gave of a vector class
that contains two overloaded operator[] functions:
        elem_t & vector_t::operator[](size_t) const { ... }
        elem_t   vector_t::operator[](size_t)       { ... }

However, sofar no-one has been able to support his or her opinion with a
clear and unambiguous reference to an authoritative source like Ellis &
Stroustrup or the 2nd. edition of Stroustrup's "The C++ Programming
Language".
E.g., Jordan Boucher, Interactive Systems Corporation, writes :
>>Is it legal C++ to have a const as well as a "non-const" member function of
>>the same (overloaded) name?
>       I don't think so; unless the parameters to the functions are different.
>       IMHO:
>       The parameters to an overloaded function are what determines its
>       uniqueness, not the return type and/or member type (const).
>       So, I think the above two operators are not unique.  I don't have the
>       ARM with me to point you to the exact reference.  But, I think you can
>       find it in there fairly quickly.

Mikael Erikson, Telia Research in Sundsvall Sweden,  also refers to
the ARM, but - noting that the discussion in ARM is somewhat unclear on
this point - reaches a conclusion that is the opposite of that of
Jordan Boucher:
>>Is it legal C++ to have a const as well as a "non-const" member function
>>of the same (overloaded) name?
>       In short, YES. The discussion on pages 316-317 in Ellis/Stroustrup
>       is somewhat unclear but it should (at least IMHO) mean that
>       the const of the method matters and apply to the object that
>       the method is called for.

What IS clear, is that there is an ongoing practice out there of
a number of programmers who are actually implementing classes that
have const and non-const member functions of the same name.

E.g. Jamshid Afshar, University of Texas at Austin, writes :
>       What you do is fine, legal, and I do it myself.  One suggestion,
>       though.  Instead of the const operator[] returning an elem_t value,
>       let it return a `const' elem_t reference -- it saves the construction
>       and destruction of a temporary.

And Steve Clamage, Taumetric Corporation, writes:
>>Is it legal C++ to have a const as well as a "non-const" member function of
>>the same (overloaded) name?
>       It is not only legal, but often necessary, since you cannot apply a
>       non-const member function to a const object.

The "authoritative literature" seems to allow for "alternative exegesis".
Therefore it would be interesting to know whether the programmers who have
implemented classes with const and non-const member functions of the same
name all have used Borland's BCC (or TCC), or whether such classes are
indeed accepted by a number of different compilers on different platforms.
This would at least give an indication of the "current actual portability" of
such code.

HSTOF@NKI.NL
Huub Stoffers, Netherlands Cancer Institute




Author: pauld@cs.washington.edu (Paul Barton-Davis)
Date: 8 Nov 91 18:40:10 GMT
Raw View
In article <1991Nov7.160927.7276@nki.nl> hstof@nki.nl writes:
>In article <1991Nov5.163825.7273@nki.nl>, I asked whether it is legal C++
>to have a const as well as a "non-const" member function of the same
>(overloaded) name?
>I have received a number of widely differing answers. Apparantly my question
>touched upon a topic that is rather ill-defined in the literature.

 [ other stuff deleted ]

Thanks to Bob Martin, I think I can offer a little light here, since I
was grappling with exactly the same problem, even if it was not so
clearly stated, over on comp.lang.c++.

What you are forgetting is that every member function has an
*implicit* parameter (this), whose type *is* modified by the prescence
or abscence of const in the function declaration. Thus:

 X::foo (int a, float b, SomeClass& c);
 X::foo (int a, float b, SomeClass& c) const;

should actually be considered to have the signatures implied by:

 X::foo (const X* this, int a, float b, SomeClass& c);
 X::foo (const X* const this, int a, float b, SomeClass& c);

which are clearly different. Therefore, I submit that the prescence of
both functions is perfectly legal and natural.

-- paul


--
"And I promised that I would forgive nothing, and change everything.
 But this was before I became trapped in the habits & haunts of this world."




Author: pal@xanadu.wpd.sgi.com (Anil Pal)
Date: 8 Nov 91 18:43:02 GMT
Raw View
In article <1991Nov7.160927.7276@nki.nl>, hstof@nki.nl writes:

 Is it legal C++ to have a const as well as a "non-const"
 member function of the same (overloaded) name?

 However, so far no-one has been able to support his or her
 opinion with a clear and unambiguous reference to an
 authoritative source like Ellis & Stroustrup or the 2nd.
 edition of Stroustrup's "The C++ Programming Language".

On p. 320-321 of the ARM, there is an example of the use of
"constness" as a tie-breaker for const member functions, with

 class X {
 public:
  void f() const;
  void f();
  // ...
 };

 void g(const X& a, X b)
 {
  a.f();  // calls X::f() const
  b.f();  // calls X::f()
 }

The closest I have seen to an explicit statement of this rule is at
the bottom of p. 316, where it states that:

 For purposes of argument matching, a nonstatic member function
 is considered to have an extra argument specifying the object
 for which it is called.

And then on p. 317, it goes on to describe the type of this extra
argument (const for const object, etc.).

The one missing link is the translation of a "const" specification on
a member function into a const modifier on the hidden formal parameter
"this", but this should be obvious given the meaning of "const" in a
member function.

--
Anil A. Pal, Silicon Graphics, Inc.
pal@wpd.sgi.com (415)-335-7279




Author: aldavi01@starbase.spd.louisville.edu (Arlie Davis)
Date: 14 Nov 91 00:22:52 GMT
Raw View
In <1991Nov8.184010.14079@beaver.cs.washington.edu> pauld@cs.washington.edu (Paul Barton-Davis) writes:

>What you are forgetting is that every member function has an
>*implicit* parameter (this), whose type *is* modified by the prescence
>or abscence of const in the function declaration. Thus:
> X::foo (int a, float b, SomeClass& c);
> X::foo (int a, float b, SomeClass& c) const;
>should actually be considered to have the signatures implied by:
> X::foo (const X* this, int a, float b, SomeClass& c);
> X::foo (const X* const this, int a, float b, SomeClass& c);

I believe this assumption is implementation-specific, and thus nonportable.
While it may be true that -every- compiler that you meet generates a
signature for member functions by silently inserting a "this" pointer, you
may not assume that this will -always- be true.

>which are clearly different. Therefore, I submit that the prescence of
>both functions is perfectly legal and natural.



>--
>"And I promised that I would forgive nothing, and change everything.
> But this was before I became trapped in the habits & haunts of this world."
--
Arlie Davis | sivaD eilrA

"Carpe grepem!"  (Ruthlessly stolen -- apologies to Jester :)

My university backs every word I say!
(And if you believe that, I've got some brand new 400MHz 80586s that I'll
sell real cheap...)




Author: pauld@cs.washington.edu (Paul Barton-Davis)
Date: 14 Nov 91 17:55:34 GMT
Raw View
In article <aldavi01.690078172@starbase.spd.louisville.edu> aldavi01@starbase.spd.louisville.edu (Arlie Davis) writes:
>In <1991Nov8.184010.14079@beaver.cs.washington.edu> pauld@cs.washington.edu (Paul Barton-Davis) writes:
>
>>What you are forgetting is that every member function has an
>>*implicit* parameter (this), whose type *is* modified by the prescence
>>or abscence of const in the function declaration. Thus:
>> X::foo (int a, float b, SomeClass& c);
>> X::foo (int a, float b, SomeClass& c) const;
>>should actually be considered to have the signatures implied by:
>> X::foo (const X* this, int a, float b, SomeClass& c);
>> X::foo (const X* const this, int a, float b, SomeClass& c);
>
>I believe this assumption is implementation-specific, and thus nonportable.
>While it may be true that -every- compiler that you meet generates a
>signature for member functions by silently inserting a "this" pointer, you
>may not assume that this will -always- be true.

I submit not:

ARM, pp. 176

 "In a nonstatic member function, the keyword "this" is a
 pointer to the object for which the function is called."

ARM, pp. 177

 "The type of "this" in a member function of a class X is X *const,
 unless the member function is declaredconst or volatile; in those
 cases, the type of "this" is const X* const or volatile X *const
 respectively."

So, I made a small error above; the implied signatures should be:

 X::foo (X* const this, int a, float b, SomeClass& c);
 X::foo (const X* const this, int a, float b, SomeClass& c);

Whether or not "this" is used to form the signature, the implied
signature (by which I mean the one used to determine which instance of
an overloaded function will be called) certainly *must* take "this"
into account. There may or may not be a "this" passed as an argument,
but the signature of

  foo () const;

and
  foo();

can never be the same, simply because the "this" pointer accessible
from within foo() has different type in each instance.

-- paul
--
"And I promised that I would forgive nothing, and change everything.
 But this was before I became trapped in the habits & haunts of this world."




Author: pal@xanadu.wpd.sgi.com (Anil Pal)
Date: 14 Nov 91 20:25:03 GMT
Raw View
In article <aldavi01.690078172@starbase.spd.louisville.edu>, aldavi01@starbase.spd.louisville.edu (Arlie Davis) writes:
|> In <1991Nov8.184010.14079@beaver.cs.washington.edu> pauld@cs.washington.edu (Paul Barton-Davis) writes:
|>
|> >What you are forgetting is that every member function has an
|> >*implicit* parameter (this), whose type *is* modified by the prescence
|> >or abscence of const in the function declaration. Thus:
|> > X::foo (int a, float b, SomeClass& c);
|> > X::foo (int a, float b, SomeClass& c) const;
|> >should actually be considered to have the signatures implied by:
|> > X::foo (const X* this, int a, float b, SomeClass& c);
|> > X::foo (const X* const this, int a, float b, SomeClass& c);
|>
|> I believe this assumption is implementation-specific, and thus nonportable.
|>
|> While it may be true that -every- compiler that you meet generates a
|> signature for member functions by silently inserting a "this" pointer, you
|> may not assume that this will -always- be true.



Author: jbn@lulea.telesoft.se (Johan Bengtsson)
Date: 15 Nov 91 13:46:54 GMT
Raw View
pal@xanadu.wpd.sgi.com (Anil Pal) writes:
>
> From E&S, p.316-317:
>
>  For purposes of argument matching, a nonstatic member function
>  is considered to have an extra argument specifying the object
>  for which it is called.

 This _should_ mean that

 TypeID type();
 and
 static TypeID type();

 are two different overloaded functions.  In an object call,
 object.type(), the first is selected.  In a static call,
 class::type(), the second is chosen.

 C++ 2.1 does not accept this.  Does any other compiler accept it?
--
-------------------------------------------------------------------------------
| Johan Bengtsson, Telia Research AB, Aurorum 6, S-951 75 Lulea, Sweden       |
| Johan.Bengtsson@lulea.telesoft.se; Voice:(+46)92075471; Fax:(+46)92075490   |
-------------------------------------------------------------------------------




Author: pal@xanadu.wpd.sgi.com (Anil Pal)
Date: 16 Nov 91 01:54:51 GMT
Raw View
In article <3834@lulea.telesoft.se>, jbn@lulea.telesoft.se (Johan Bengtsson) writes:
|> pal@xanadu.wpd.sgi.com (Anil Pal) writes:
|> >
|> >  For purposes of argument matching, a nonstatic member function
|> >  is considered to have an extra argument specifying the object
|> >  for which it is called.
|>
|>  This _should_ mean that
|>
|>  TypeID type();
|>  and
|>  static TypeID type();
|>
|>  are two different overloaded functions.

Interesting point.  The problem, of course, is that within a member
function, a call of

 type();

could refer to either the static function, or (using the implicit
this) to the non-static function.  I wouldn't like to lose the
capability to use both static and non-static functions in this way
(i.e. without explicit qualification as class::type() or as
this->type()), and so I can live without this overloading.  But
perhaps the standard needs to make this explicit, and explain why this
overloading is not allowed.

--
Anil A. Pal, Silicon Graphics, Inc.
pal@wpd.sgi.com (415)-335-7279




Author: jamshid@ut-emx.uucp (Jamshid Afshar)
Date: 18 Nov 91 01:49:40 GMT
Raw View
In article <3834@lulea.telesoft.se> jbn@lulea.telesoft.se (Johan Bengtsson) writes:
>pal@xanadu.wpd.sgi.com (Anil Pal) writes:
>>
>> From E&S, p.316-317:
>>
>>  For purposes of argument matching, a nonstatic member function
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>  is considered to have an extra argument specifying the object
>>  for which it is called.
>
> This _should_ mean that
>
> TypeID type();
> and
> static TypeID type();
>
> are two different overloaded functions.

ARM 9.4 p179:
 There cannot be a static and a nonstatic member function with
 the same name and the same argument types.
and ARM 13 (Overloading) p309
 Member functions that differ only in that one is a static member
 and the other isn't may not have the same name.

> In an object call,
> object.type(), the first is selected.  In a static call,
> class::type(), the second is chosen.

Note that `object.type()' is also a valid call to Object::type(), even
if type() is a static member function.  Also, rules would have to be
created about what function is called by the statements "type();" and
"Object::type();" inside another member function of Object or of
classes derived from Object.

While the ARM doesn't seem to explicitly say why overloading based on
"staticness" is disallowed, I imagine the reason is that it would have
caused too much confusion for both the class programmer and the client
programmer while offering no signficant advantages.  The class
programmer would have to somehow explicitly disambiguate the use of
the overloaded function in other member functions, and clients would
have to remember that

 object.type();
and Object::type();

may call different functions.

Jamshid Afshar
jamshid@emx.utexas.edu




Author: jimad@microsoft.com (Jim ADCOCK)
Date: 17 Nov 91 21:11:44 GMT
Raw View
In article <3834@lulea.telesoft.se> jbn@lulea.telesoft.se (Johan Bengtsson) writes:
|pal@xanadu.wpd.sgi.com (Anil Pal) writes:
|>
|> From E&S, p.316-317:
|>
|>  For purposes of argument matching, a nonstatic member function
|>  is considered to have an extra argument specifying the object
|>  for which it is called.
|
| This _should_ mean that
|
| TypeID type();
| and
| static TypeID type();
|
| are two different overloaded functions.  In an object call,
| object.type(), the first is selected.  In a static call,
| class::type(), the second is chosen.

Ah, but then in another member function calling your function with
an implied "this", is the the "this" actually to be implied, or not?

Read ARM page 317 and see what Stroustrup has to say about resolving
overloaded member function ambiguities when invoking with an  implied "this."




Author: jbn@lulea.telesoft.se (Johan Bengtsson)
Date: 20 Nov 91 11:30:46 GMT
Raw View
jimad@microsoft.com (Jim ADCOCK) writes:
< jbn@lulea.telesoft.se (Johan Bengtsson) writes:
< |pal@xanadu.wpd.sgi.com (Anil Pal) writes:
< |<
< |<  For purposes of argument matching, a nonstatic member function
< |<  is considered to have an extra argument specifying the object
< |<  for which it is called.
< |
< | TypeID type();
< | static TypeID type();
<
< Ah, but then in another member function calling your function with
< an implied "this", is the the "this" actually to be implied, or not?

 Good point.  Suggestion:

 If the other member function is static, then call the static
 type().  If the other member is non-static call the non-static
 type().

 This seems very natural to me.

--
-------------------------------------------------------------------------------
| Johan Bengtsson, Telia Research AB, Aurorum 6, S-951 75 Lulea, Sweden       |
| Johan.Bengtsson@lulea.telesoft.se; Voice:(+46)92075471; Fax:(+46)92075490   |
-------------------------------------------------------------------------------