Topic: super in C++
Author: dwalker07@snet.net.invalid (Daryle Walker)
Date: Sun, 10 Sep 2000 00:28:06 GMT Raw View
Roger Bonzer <rogerb@imagebuilder.com> wrote:
> In article <8F9F78A99youdontwannaknow@194.134.193.17>, Martijn Lievaart
> <xnews-public@greebo.orion.nl> wrote:
> > I would love to see this feature, exactly because of the maintenace
> > nightmares it saves. I have no opinion (yet) on the issue of the ambigious
> > foo(). On one hand, the solution proposed by Greg doesn't "feel" right, but
> > that has mainly to do with the syntax he proposes. OTOH, this ambiguity
> > will arise in practice.
> How about something like this:
I think I suggested something like this in this thread (or a similar
one).
> inherited::foo names the foo in any of the parent classes. if a valid
> match is found in more than one parent class, it is a syntax error.
Sounds good. Based on what you say next, this first rule applies to the
direct parent classes and the indirect ones, right? A minor point to
mention could be that an ambiguity error also appears if the class
chosen for appears more than once (unless virtual inheiritance reduces
all those appearances to one).
> inherited<parentX>::foo names the foo in parent class parentX. if
> parentX is not an immediate parent class, or if parentX is an immediate
> parent class more than once, or if a valid match is not found in
> parentX, it is a syntax error.
Can a class have a direct parent that appears multiple times?
I don't think you should ban a non-direct parent class for your second
rule, as long as it has an unambiguous appearance. I envision the
second rule as an application of the first rule, where the search is
just the first rule applied only to the parentX branch of the original
class. The ambiguity problem I described in the first rule won't apply
if the parentX branch has at most one instance of the indirect parent
class and any other instances are part of other direct parent branches.
You could even allow chaining of "inheirted" parts. Something like:
inherited<parentX>::inherited<greatgrandparentY>::bar
(Note that I skipped a generation; a demostration of my modification to
the second rule.)
--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net
---
[ 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 ]
Author: David R Tribble <david@tribble.com>
Date: Tue, 5 Sep 2000 17:00:14 GMT Raw View
Martijn Lievaart at xnews-public@greebo.orion.nl wrote:
>> On a side note, does anyone really use the "feature" of defining a
>> body for a pure virtual function? I personally would not mind this
>> "feature" to be scrapped. You can always use a different name for
>> your function (foo_default()?).
I use it to track down implementation errors, where I forgot to
implement/override member functions in derived classes:
class Base
{
public:
virtual int foo() = 0;
...
};
int Base::foo()
{
cerr << "Base::foo() is not implemented\n";
return 0; // or throw...
}
This plugs a hole in the C++ specification, where fogetting to
provide a definition of a virtual member function results in
undefined behavior. Some linkers don't complain about this, and
you get a runtime SEGV (attempting to invoke a function through a
null pointer).
--
David R. Tribble, mailto:david@tribble.com, http://david.tribble.com
---
[ 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 ]
Author: xnews-public@greebo.orion.nl (Martijn Lievaart)
Date: Wed, 6 Sep 2000 11:28:53 GMT Raw View
david@tribble.com (David R Tribble) wrote in
<39B52397.11FF9A5@tribble.com>:
>Martijn Lievaart at xnews-public@greebo.orion.nl wrote:
>>> On a side note, does anyone really use the "feature" of defining a
>>> body for a pure virtual function? I personally would not mind this
>>> "feature" to be scrapped. You can always use a different name for
>>> your function (foo_default()?).
>
>I use it to track down implementation errors, where I forgot to
>implement/override member functions in derived classes:
>
[...]
>
>This plugs a hole in the C++ specification, where fogetting to
>provide a definition of a virtual member function results in
>undefined behavior. Some linkers don't complain about this, and
>you get a runtime SEGV (attempting to invoke a function through a
>null pointer).
>
This is very good advice. Thank you!
M4
---
[ 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 ]
Author: xnews-public@greebo.orion.nl (Martijn Lievaart)
Date: Wed, 6 Sep 2000 21:15:06 GMT Raw View
nospam@nospam.com (Chip Jarred) wrote in
<B5D5E462.811E%nospam@nospam.com>:
>in article 8F9F78A99youdontwannaknow@194.134.193.17, Martijn Lievaart at
>xnews-public@greebo.orion.nl wrote on 8/29/00 2:38 PM:
>
>> On a side note, does anyone really use the "feature" of defining a
>> body for a pure virtual function? I personally would not mind this
>> "feature" to be scrapped. You can always use a different name for your
>> function (foo_default()?).
>
>It's not likely to be scrapped, but it wouldn't break my heart either...
>there is another way to do it. You simply provide your implementation
>in another protected member function, and if subclasses need it, they
>call that other member function. Still, I do on occasion use the
>ability to provide an implementation for a pure virtual.
>
I think Davids answer to this is definitive. Removing it will break some
programs that cannot be fixed. So removing this feature is out. To bad, to
sad.
[ discussion inherited vs other syntax ]
Well there is a precedent for changing the then natural operation of some
symbols, operator<< and >>. So I don't see introducing some form of ^ as
meaning "up" (in the inheritence tree) as a problem.
You are right that introducing a new keyword would not break /that/ many
programs, and on second thought, the programs it does break are very
probably the ones that emulate this feature anyhow. So the people
maintaining those programs might be less anoyed than someone who has to
rewrite all for loops in a 300KLOC project ;^>.
I still like the idea of using existing tokens in a new way, because:
a) It won't break existing programs, and
b) imho it stands out in the program, making the intent clearer, and
c) it's easier to type.
But I don't realy care to much one way or the other.
M4
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/28 Raw View
Sorry about the delay in replying... this little thing called "work" has
been occupying my time :-)
in article 8o0mba$1ea7$1@news.hal-pc.org, Greg Brewer at
nospam.greg@brewer.net wrote on 8/23/00 9:31 AM:
> "Chip Jarred" <nospam@nospam.com> wrote in message
> news:B5C7B3E8.7D15%nospam@nospam.com...
>> in article 8nmbkr$1bc6$1@news.hal-pc.org, Greg Brewer at
>> nospam.greg@brewer.net wrote on 8/21/00 3:45 PM:
>> Are you advocating that pure virtual functions be forbidden from being
>> implemented (or that they should cause warnings)? Or are you recommending
>> some additional syntax that would indicate that a given pure virtual
>> functions does not have a implementation. If the former, I have to
> strongly
>> disagree... pure virtual means only that subclasses must implement it, and
>> in now way should imply whether or not the base class implements it. If
> the
>> latter, well, I don't have a problem with the concept having some sort of
>> "not implemented" specifier.
>
> Definitely the later. As I previously stated, syntax such as "virtual int
> Foo(void) =0{};" would be used it mark a pure virtual function.
While I'm not convinced of the necessity, I'm not particularly opposed to
this.
>> Personally, I think that if a pure virtual function is causing ambiguity
>> between something like "inherited::foo()" (or "this-->foo()", though I
> still
>> don't like that approach)
>
> then lets drop it and stick with inherited!
Cool.
>> , the situation is not any clearer than if a
>> non-pure virtual function caused the ambiguity. This is a case where you
>> bite the bullet and explicitly specify a base class, just as you do now.
> My
>> reason for this are simple. Multiple inheritance is a great feature, but
> it
>> also has a dark side. Complexity can grow very fast, and so when using
>> multiple inheritance, I really want very, very simple resolution rules so
>> that problems inherent in multiple inheritance are not complicated further
>> by compiler resolution rules. I compare our rules here
>>
>> Your proposed rule: ambiguity occurs if there is more than one
>> inheritance path that can be matched to the "inherited::" member field or
>> member function access, excepting the case of member functions, if all but
>> one of those inheritance paths match to pure virtual member functions.
>
> No, my rules would be more like
>
> 1) pure virtual functions may be marked as unimplemented by placing
> braces ("{}") after the pure virtual
> marker. If an implementation of a member function is present then an
> exception occurs.
So this is something that must be checked at runtime (inferred from the use
of "exception" rather than "error")? I suppose it could also be checked at
compile time, but only for the module which implements the pure virtual
member function. Other modules could only check to ensure that there is no
call to it. I would prefer a compile time check to a runtime check. You
find out about it sooner, and every module must be compiled, but it is not
guaranteed that the pure virtual function will ever been referenced at
runtime. But then for a pure virtual function, the only way to call it
would be via an explicit scope specification or via the "inherited::"
mechanism, either of which could be determined at compile time resulting in
a linker error anyway.
> 2) in a member function, function calls prefixed with "inherited::" will
> search the inheritance path for a
> matching member field excluding pure virtual functions marked as
> unimplemented. Ambiguity occurs if more
> than one exact match is found or when an no exact match is found and more
> than one inexact match is found.
>
> The search specifically excludes pure virtuals marked as unimplemented.
> Also, if one branch defines foo(int) while another branch defines foo(long)
> and the call is inherited::foo(int) then no ambiguity exists. On the other
> hand, a call to inherited::foo(short) is ambiguous since both are inexact
> matches.
I appreciate the clarification, but I still prefer my simpler rule. Under
your rule, I wouldn't have to deal with it because I would never use the
unimplemented pure virtual syntax (even when I choose not to implement it),
excepting of course, when I have to use a third party library which might
have used it. And then I end up with more complications in the case of
multiple inheritance, and I'd really, really prefer to keep the situation as
simple as possible.
I guess the main problem is that I'm still not convinced of it's necessity
and I'd like to see that something is necessary before adding it to the
language.
>> My proposed rule: ambiguity occurs if there is more than one
>> inheritance path that can be matched to the "inherited::" member field or
>> member function access.
>
> I skipped the rest of your posting. The next couple of paragraphs seemed to
> assume the first of the two alternatives you gave in paragraph 1. I find it
> reasonable to want to call a pure virtual function only if that function may
> be implemented. When the function is precluded from having a definition
> then it is unreasonable to want to call it.
Yes, you were right that I was somewhat under the impression that you were
for the first of the alternatives. However, I believe that a good bit of
what I posted is still relevant...
In support of your position that properly specificied pure virtuals member
functions should not cause ambiguity in an "inherited::" call, you gave an
example in which you used inheritance to solve a specific class of problems.
I countered by showing that a different design was not only more flexible,
but also avoided the nasty ambiguities without having to specially mark any
pure virtual member functions. In fact, there are a great many nasty things
which can occur in the case of multiple inheritance, and would regardless of
the language features that might be added, but they can nearly always be
avoided by adopting a better design. In way the awkwardness of a given
design can serve as a softer version of a compiler warning... if it seems
really awkward to do something, maybe you shouldn't be doing it... or at
least not that way.
The way I see it, what is happening here is that you and I (and whoever else
chimes in) are having ante-pre-standardization arguments about a feature
which we hope one day will be foramlly analyzed for inclusion in the
language. We (you and I anyway) agree that something like "inherited::" is
needed. We disagree about the conditions under which it should cause a
compiler ambiguity error. You see the feature of specifying unimplemented
pure virtuals and having inherited:: resolve around them as essential (or at
least overwhelmingly compelling). I don't see it as so compelling, and in
redards to the resolution of inherited::, that it might even promote poor
designs (not that C++ goes to great lengths to prevent that anyway). IMHO
the burden is on you to show why it is so overwhelmingly compelling. You
gave one example, and I showed why it's not necessary in that example. So
the ball's back in your court.
Regrettably in the last few volleys of posts, it has been just you and I
contributing. I would like to know what others think of this, and see where
they stand. In the earlier posts it seemed that a few respondants felt as
you and I do that something like "inherited::" should be made part of the
language, but an equally few respondants were ambivalent, arguing that the
same effects can be achieved with current language features. We have
pointed out that the mechanisms through which one achieves a similar result
has potential maintenance problems, and that calling an inherited version of
a member function is such a common OO idiom that it deserves special
language support (much as accessing a member via a pointer to a struct or
class is so idiomatic that it warrants having the p->x syntax instead of
requiring (*p).x). We have not really heard counter arguments to this.
Does anyone else out there have an opinion?
Chip Jarred
---
[ 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 ]
Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 2000/08/29 Raw View
> Sorry about the delay in replying... this little thing called "work" has
> been occupying my time :-)
Hah, I've heard that one before! Shoot, I've used it before!! :-)
> > 1) pure virtual functions may be marked as unimplemented by placing
> > braces ("{}") after the pure virtual
> > marker. If an implementation of a member function is present then an
> > exception occurs.
>
> So this is something that must be checked at runtime (inferred from the
use
> of "exception" rather than "error")? I suppose it could also be checked
at
> compile time, but only for the module which implements the pure virtual
> member function. Other modules could only check to ensure that there is
no
> call to it. I would prefer a compile time check to a runtime check. You
> find out about it sooner, and every module must be compiled, but it is not
> guaranteed that the pure virtual function will ever been referenced at
> runtime. But then for a pure virtual function, the only way to call it
> would be via an explicit scope specification or via the "inherited::"
> mechanism, either of which could be determined at compile time resulting
in
> a linker error anyway.
I should have said error; I had exceptions on my mind at the time I wrote
it. Checking are compile time is precisely what I have in mind. Runtime
checking is unnecessary since it can't happen. It would be tested in the
same manner as
class X { virtual void foo(void) {}};
void X::foo(void) {}
is tested; just a slightly different error message.
> > 2) in a member function, function calls prefixed with "inherited::"
will
> > search the inheritance path for a
> > matching member field excluding pure virtual functions marked as
> > unimplemented. Ambiguity occurs if more
> > than one exact match is found or when an no exact match is found and
more
> > than one inexact match is found.
> >
> > The search specifically excludes pure virtuals marked as unimplemented.
> > Also, if one branch defines foo(int) while another branch defines
foo(long)
> > and the call is inherited::foo(int) then no ambiguity exists. On the
other
> > hand, a call to inherited::foo(short) is ambiguous since both are
inexact
> > matches.
>
> I appreciate the clarification, but I still prefer my simpler rule. Under
> your rule, I wouldn't have to deal with it because I would never use the
> unimplemented pure virtual syntax (even when I choose not to implement
it),
> excepting of course, when I have to use a third party library which might
> have used it. And then I end up with more complications in the case of
> multiple inheritance, and I'd really, really prefer to keep the situation
as
> simple as possible.
>
> I guess the main problem is that I'm still not convinced of it's necessity
> and I'd like to see that something is necessary before adding it to the
> language.
>
> >> My proposed rule: ambiguity occurs if there is more than one
> >> inheritance path that can be matched to the "inherited::" member field
or
> >> member function access.
> >
> > I skipped the rest of your posting. The next couple of paragraphs
seemed to
> > assume the first of the two alternatives you gave in paragraph 1. I
find it
> > reasonable to want to call a pure virtual function only if that function
may
> > be implemented. When the function is precluded from having a definition
> > then it is unreasonable to want to call it.
>
> Yes, you were right that I was somewhat under the impression that you were
> for the first of the alternatives. However, I believe that a good bit of
> what I posted is still relevant...
>
> In support of your position that properly specificied pure virtuals member
> functions should not cause ambiguity in an "inherited::" call, you gave an
> example in which you used inheritance to solve a specific class of
problems.
> I countered by showing that a different design was not only more flexible,
> but also avoided the nasty ambiguities without having to specially mark
any
> pure virtual member functions. In fact, there are a great many nasty
things
> which can occur in the case of multiple inheritance, and would regardless
of
> the language features that might be added, but they can nearly always be
> avoided by adopting a better design. In way the awkwardness of a given
> design can serve as a softer version of a compiler warning... if it seems
> really awkward to do something, maybe you shouldn't be doing it... or at
> least not that way.
>
> The way I see it, what is happening here is that you and I (and whoever
else
> chimes in) are having ante-pre-standardization arguments about a feature
> which we hope one day will be foramlly analyzed for inclusion in the
> language. We (you and I anyway) agree that something like "inherited::"
is
> needed. We disagree about the conditions under which it should cause a
> compiler ambiguity error. You see the feature of specifying unimplemented
> pure virtuals and having inherited:: resolve around them as essential (or
at
> least overwhelmingly compelling). I don't see it as so compelling, and in
> redards to the resolution of inherited::, that it might even promote poor
> designs (not that C++ goes to great lengths to prevent that anyway). IMHO
> the burden is on you to show why it is so overwhelmingly compelling. You
> gave one example, and I showed why it's not necessary in that example. So
> the ball's back in your court.
>
> Regrettably in the last few volleys of posts, it has been just you and I
> contributing. I would like to know what others think of this, and see
where
> they stand. In the earlier posts it seemed that a few respondants felt as
> you and I do that something like "inherited::" should be made part of the
> language, but an equally few respondants were ambivalent, arguing that the
> same effects can be achieved with current language features. We have
> pointed out that the mechanisms through which one achieves a similar
result
> has potential maintenance problems, and that calling an inherited version
of
> a member function is such a common OO idiom that it deserves special
> language support (much as accessing a member via a pointer to a struct or
> class is so idiomatic that it warrants having the p->x syntax instead of
> requiring (*p).x). We have not really heard counter arguments to this.
> Does anyone else out there have an opinion?
I agree that the burden is on me to demonstrate a need. I just can't figure
out how to accomplish this. I commonly use a technique imploying pure
virtual functions as a stub for some undertermined operation. I find this
to be a very useful and powerful way. In these instances, there is no
ambiguity because the pure virtual function is never used; indeed I didn't
know that a pure virtual function could have an implementation. Now that I
know, I have suggested a mechanism that solves the problem. Without this
exception, I cannot use inherited because almost all uses will be ambiguous.
On the other hand, I will probably never use it one way or the other. I am
slow to upgrade my compilers because I have yet to have an upgrade that
would compile my existing code. By the time what I want can be canonized
and implemented, I expect to have made my fortune and retired.
I too would like to hear from some others on the subject.
Greg
---
[ 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 ]
Author: xnews-public@greebo.orion.nl (Martijn Lievaart)
Date: 2000/08/29 Raw View
nospam@nospam.com (Chip Jarred) wrote in
<B5CE26E7.7EAE%nospam@nospam.com>:
>
>Regrettably in the last few volleys of posts, it has been just you and I
>contributing. I would like to know what others think of this, and see
>where they stand. In the earlier posts it seemed that a few respondants
>felt as you and I do that something like "inherited::" should be made
>part of the language, but an equally few respondants were ambivalent,
>arguing that the same effects can be achieved with current language
>features. We have pointed out that the mechanisms through which one
>achieves a similar result has potential maintenance problems, and that
>calling an inherited version of a member function is such a common OO
>idiom that it deserves special language support (much as accessing a
>member via a pointer to a struct or class is so idiomatic that it
>warrants having the p->x syntax instead of requiring (*p).x). We have
>not really heard counter arguments to this. Does anyone else out there
>have an opinion?
>
I would love to see this feature, exactly because of the maintenace
nightmares it saves. I have no opinion (yet) on the issue of the ambigious
foo(). On one hand, the solution proposed by Greg doesn't "feel" right, but
that has mainly to do with the syntax he proposes. OTOH, this ambiguity
will arise in practice.
On a side note, does anyone really use the "feature" of defining a body for
a pure virtual function? I personally would not mind this "feature" to be
scrapped. You can always use a different name for your function
(foo_default()?).
Another issue is that adding a new keyword is sure to break a lot of
sources. Maybe we could use something else? Like a new token? I thought
about "^::foo()" but that can occur legally in a normal program. How about
"::^foo()" ?
M4
---
[ 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 ]
Author: Roger Bonzer <rogerb@imagebuilder.com>
Date: 2000/08/30 Raw View
In article <8F9F78A99youdontwannaknow@194.134.193.17>, Martijn Lievaart
<xnews-public@greebo.orion.nl> wrote:
>
> I would love to see this feature, exactly because of the maintenace
> nightmares it saves. I have no opinion (yet) on the issue of the ambigious
> foo(). On one hand, the solution proposed by Greg doesn't "feel" right, but
> that has mainly to do with the syntax he proposes. OTOH, this ambiguity
> will arise in practice.
How about something like this:
inherited::foo names the foo in any of the parent classes. if a valid
match is found in more than one parent class, it is a syntax error.
inherited<parentX>::foo names the foo in parent class parentX. if
parentX is not an immediate parent class, or if parentX is an immediate
parent class more than once, or if a valid match is not found in
parentX, it is a syntax error.
---
[ 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 ]
Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 2000/08/21 Raw View
"Chip Jarred" <nospam@nospam.com> wrote in message
news:B5C2FC60.7C79%nospam@nospam.com...
> in article 8njmb8$ife$1@news.hal-pc.org, Greg Brewer at
> nospam.greg@brewer.net wrote on 8/18/00 11:06 AM:
> [snip]
> > I disagree that it must be ambiguous. What is needed then is a
mechanism
> > for telling the compiler that the pure virtual function has no
definition
> Hmmm.... I think adding a keyword "inherited" or something like it would
be
> beneficial enough to justify it, but I think adding a keyword or some
other
> syntax to tell the compiler that the that there is no implementation for a
> particular pure virtual member function is less compelling. Yes it would
> accomplish what you want to do, but then there's nothing to prevent
someone
> later adding an implementation of the pure virtual function in another
> source file, which is only resolved at link time. If this person did not
> remember to update the class definition in the header file, the compiler
> would have no way of knowing that it should now find an ambiguity, and so
it
> will resolve it, and then linker will silently link to the wrong inherited
> member function. This is situation is subtle enough and difficult enough
to
> debug, that I would prefer being forced to explicitly resolve it by having
> the compiler generate an error.
If the person did not remember to update the header file then the compiler
should issue an error. It would be no different than
class A {public: virtual int Foo(void) {return 0;}};
void A::Foo(void) {return 1;}
where the compiler issues an error because A::Foo(void) is already defined.
In this case the syntax of the declaration states that there should be no
definition for the function. If someone attempts to define one then the
compiler should say so.
Without the ability to tell it to ignore a particular pure virtual function,
the feature would be a lot less useful. As an example, my programs
frequently must act on many objects. The object may be every data object on
a window or every collection point in a database. The action may be reading
values or performing a validation check, or estimating missing data. To
perform the process, I defined a class
class ProcessAll {public: virtual void Process(void) = 0;}
and functions that will go through all of the objects. The functions all
take a ProcessAll pointer. Now, lets say I want to perform a validation
check. I have a class that will validate 1 collection point. I combine
that class with ProcessAll class and pass that pointer to the function the
goes through all of the collection points. Now, lets say I want to estimate
missing data. I have a class that will estimate for 1 collection point. I
combine that class with ProcessAll class and pass that pointer to the
function that goes through all the collection points. The power of this is
incredable. In the combined class, I have to define the Process function
and tell it to pass execution up to the base class. I think it would be
nice if I could just pass it up without naming the base class -- hince this
suggestion. But if I have no way of telling it to ignore one of the base
classes, I have an ambiguity and can't use the method. The thing is, there
is never a definition for the pure virtual function!
> > But, if a syntactical method for handling it can be established then
there
> > is no need for a new keyword. That is why I suggested "this" + "-" +
"-" +
> > ">" in my original post. I do think that "this-->Foo()" might be too
easily
> > confused with "this->Foo()", it seems to me that the second form is
rare. I
> > would expect that it might be too easy for someone to code "this->Foo()"
> > when they mean "this-->Foo()" and create an infinite loop. But I find
it
> > preferable to adding still another keyword which would break someones
> > existing code.
>
> I applaud your effort to try to figure out a way to make this "inherited"
> feature work by extending the syntax, but that should only be done when it
> makes some degree of sense. Someone else suggested the user of the word
> static as a scope specifier for this (like static::Foo()). I don't like
> that because static has an understood meaning that has nothing to do with
> accessing inherited members. So exactly what there evils of adding a
> keyword?
My experience with the addition of the keyword true makes me philosophically
opposed to adding keywords. I see it as no different than calling a pointer
to a member function -- the "this->*Function()" syntax. In this case,
nothing other than the member functions should be allowed to call the base
functions seems a reasonable restrition. So if "this->Foo()" calls Foo() as
inherited then "this-->Foo()" would be telling the compiler to move up one
level (ie "--" suggests subtracting one level) and call a function. This is
not an idea that I have a particularly strong attachment to; I just thought
others might see merit in it.
> The answer is quite simply that they remove a potential symbol for
> the user's name spaces. If you already have a variable or function called
> inherited, then adding the inherited keyword will break it.
I was thinking more of the company with the largest market share that has
been known to disable new features by default because it would break there
own code. Personally, I think an acceptable compromise for them is to
accept the old coding and issue an "obsolete code" warning. Since I don't
use that compiler, it is no big deal for me.
> category. I also believe that whatever means we use should be consistent
> with the current syntax. With the current syntax you call a inherited
> function by calling it with the following form
>
> scope::func();
>
> So whatever means is introduced to call inherited functions without
> specifying the specific class should still keep the same basic form.
> inherited::foo() does just that.
>
> As for the problems of using "this"... this is a pointer. We don't call
> inherited functions via pointers. Actually the most common place where
we'd
> want to use this feature is for an overridden virtual function to call an
> inherited implementation of itself. In that case, what happens when you
> transform the the pointer (or reference) into a pointer(or reference) of
the
> base class? The answer is that it still behaves polymorphically. Despite
> being called the base class, it is actually the derived class, so the
> derived class's implementation is called. OOP depends on this. So what
is
> "this". "this" is simply a pointer to this object. It is provided so
that
> the object can refer to itself. In the observer pattern, objects which
are
> observing (listening) to other objects, need to pass a pointer to
themselves
> to the objects they are "observing" so that when observed object changes,
it
> can call back to the observer. That's what this is intended for, and it
is
> vitally important that this behave polymorphically like other pointers. I
> believe that "this" has the wrong meaning to specify a class scope, which
is
> what calling an inherited function is really all about.
The syntax I advocate is specific to the this pointer. "this-->Foo()" would
be the only usage allowed. Something like
baseclass *bc = this;
bc-->Foo();
would be disallowed as would "((baseclass *)this)->Foo()".
> Besides, in your own example you demonstrate that a syntax like
> "this-->Foo()" is so similar to "this->Foo()" that it would be a common
> mistake resulting in infinite loops... actually it wouldn't. It would
> result in infinite recursion, which would cause the program to crash as it
> rapidly consumed the entire stack.
Actually, it is an infinite loop. Since it is a loop that consumes a finite
resource, it is true that it will eventually crash. But it still meets the
definition of an infinite loop since ordinary execution will never result in
the loop exiting.
> Yet, you say you prefer it to a keyword.
> Why? What you are saying is that you would prefer an obviously more bug
> prone syntax over one that would be hard to misuse. And yet the
motivation
> for such a language feature is to minimize a certain class of bugs by
> automating a particular programming practice. So why implement such a
> bug-prevention feature in a way that is itself bug prone? If this is just
a
> knee jerk reaction that new keywords are bad, I think you have to put that
> in perspective. If there is enough benefit, a new keyword can be a good
> thing. It's like knee jerk reactions to goto statements. Yes, most of
the
> time the knee jerk reaction is correct, and to date, I haven't seen a good
> use of goto in C++, but I have seen good uses of it in C. I think that
> those knee jerk responses need to prompt us to look at things more
> carefully, not dismiss them out of hand. What is the goal of a good
> programming language anyway? Is it to meet some theoretical concept of
> language minimalism, or is it to help real programmers write correct,
> efficient programs? Often the two go hand in hand, and are thoroughly
> compatible goals, but but on the whole, I believe that the latter is far
> more important than the former.
I believe I've covered all of this.
Greg
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/22 Raw View
in article 8nmbkr$1bc6$1@news.hal-pc.org, Greg Brewer at
nospam.greg@brewer.net wrote on 8/21/00 3:45 PM:
> If the person did not remember to update the header file then the compiler
> should issue an error. It would be no different than
> class A {public: virtual int Foo(void) {return 0;}};
> void A::Foo(void) {return 1;}
> where the compiler issues an error because A::Foo(void) is already defined.
> In this case the syntax of the declaration states that there should be no
> definition for the function. If someone attempts to define one then the
> compiler should say so.
Are you advocating that pure virtual functions be forbidden from being
implemented (or that they should cause warnings)? Or are you recommending
some additional syntax that would indicate that a given pure virtual
functions does not have a implementation. If the former, I have to strongly
disagree... pure virtual means only that subclasses must implement it, and
in now way should imply whether or not the base class implements it. If the
latter, well, I don't have a problem with the concept having some sort of
"not implemented" specifier. I don't see a lot of value in it, as you
obviously do, but I wouldn't be dead set against such a feature. If I were,
it would be because of a particular way it would be implemented.
Personally, I think that if a pure virtual function is causing ambiguity
between something like "inherited::foo()" (or "this-->foo()", though I still
don't like that approach), the situation is not any clearer than if a
non-pure virtual function caused the ambiguity. This is a case where you
bite the bullet and explicitly specify a base class, just as you do now. My
reason for this are simple. Multiple inheritance is a great feature, but it
also has a dark side. Complexity can grow very fast, and so when using
multiple inheritance, I really want very, very simple resolution rules so
that problems inherent in multiple inheritance are not complicated further
by compiler resolution rules. I compare ours rules here (using
"inherited::" instead of "this-->" but for this particular point, they are
interchangeable).
Your proposed rule: ambiguity occurs if there is more than one
inheritance path that can be matched to the "inherited::" member field or
member function access, excepting the case of member functions, if all but
one of those inheritance paths match to pure virtual member functions.
My proposed rule: ambiguity occurs if there is more than one
inheritance path that can be matched to the "inherited::" member field or
member function access.
Your rule has one exception. Mine has none. Let's also keep in mind that
the difference only becomes and issue when you have multiple base classes
with member functions (not necessarily virtual, mind you!) that have the
same signatures. The subclass which is the unfortunate product of such an
unholy marriage has the unenviable task of attempting to work polymorphicly
correctly anywhere the corresponding base classes can be used. This
situation is best avoided when possible, and therefore should be reasonably
rare, but obviously it will come up from time to time and when it does, I
believe the programmer has his hands full enough without to worry about
without introducing more complexity, even if it is just one exception. If a
programmer finds that this comes up a lot in his programming, he probably
needs to take another look at his design (just as a goto should prompt
taking another look at design, particularly when there are lots of them).
Furthermore, since the whole point of something like "inherited::" would be
to access inherited implementations, one very common result will be that it
is used to access an implementation which happens to be declared as pure
virtual.
Take this common single inheritance case:
class A
{
public:
virtual void Foo(void) = 0;
};
void A::Foo(void)
{
// Default implementation here
}
--------
class B : public class A
{
public:
virtual void Foo(void);
};
With current syntax, a very common implementation of B::Foo() might look
like this:
void B::Foo(void)
{
A::Foo();
// Do class B specific stuff here
}
This is legal, and very common.
However, the whole point of having something like "inherited::" would be to
do the following
void B::Foo(void)
{
inherited::Foo();
// Do class B specific stuff here
}
With your proposal, you now have a problem. inherited::Foo() refers to a
pure virtual function, but that's exactly what I want it to do! The whole
point is that I want the inherited Foo(), and I want to do it in such a way
that if I change the hierarchy, putting an intermediate class C between A
and B, it still does the right thing without changing the code. I don't
care about exactly where it comes from (so long as it's the one the
immediate base class would use) or even if it's declared pure virtual or
not.
If you agree that this should be allowed, then you have to modify the rule
to allow for another exception (that's now two, not just one). You'd have
to add "unless there is only one inherited pure virtual function, in which
case that one is called."
I suggest that this usage is far, far more common than the case where you
have multiply inherited functions of the same signature where all but one
are pure virtual.
My rule works just as well with this as it does with the multiple
inheritance case. Having not done a formal study of it's usage industry
wide, my guesstimate is that this would take care of 95% to 98% of the cases
where calling an inherited function is needed. I can believe that there is
2% to 5% where it you would still need to use explicit scope specifiers. It
is simpler to implemenn that your scheme, and on the whole I think helps
identify more potential bugs at compile time in the 2%-5% of the time when
you do have multiply inherited functions with the same signature. On the
whole, I think it's a safer approach. But let's look at a problem that is
in that 2%-5% range. Let's introduce a class M to the example I gave above
(this is done after A & B were already written, and possibly in use for a
while, so their design is possibly not as fresh in the programmer's mind as
when they were first written).
// X isn't important, just here to imply a potentially
// more complicated hierarchy;
class M : public X
{
public:
virtual void Foo();
};
void::Foo(void)
{
inherited::Foo(); // class X's Foo
// Specific implementation for class M
};
Now let's modify B like this:
class B : public A, public M
{
public:
virtual void Foo();
};
B::Foo() is implemented the same as before.
What happens under my rule is that you get a compiler error that the call
inherited::Foo() in B::Foo() is ambiguous. Result: the programmer takes
another look at A and B's definitions, thinks about it, provides an explicit
scope specifier, and he's finished (after testing, of course!).
What happens under your rule is that the compiler sees that class A::Foo()
is pure virtual (even though it is implemented, but the compiler doesn't
know that when processing B, which is commonly in a different module from
A's implementation). So even though before the addition of class M, it
would call A::Foo(), it will now call M::Foo(), which radically alters the
behavior, especially if the idea behind M::Foo() has nothing to do with
A::Foo(). So it silently compiles and the programmer thinks he's done
something slick. If he's lucky, when he tests it, he notices the results
are not what he expected, so he spends a little time debugging, discovers
the problem, and fixes it quickly. If he's not lucky, the bug is subtle, it
escapes his attention, and later, perhaps weeks or months after he's written
the code, some tester realizes that the program isn't working right, and the
programmer spends hours and hours, maybe days and weeks, debugging to find
that the problem is that compiler is silently doing something he didn't
expect. Or worse, no one notices, and the program is shipped with the bug,
giving wrong results to who knows how many customers.
> Without the ability to tell it to ignore a particular pure virtual function,
> the feature would be a lot less useful. As an example, my programs
> frequently must act on many objects. The object may be every data object on
> a window or every collection point in a database. The action may be reading
> values or performing a validation check, or estimating missing data. To
> perform the process, I defined a class
> class ProcessAll {public: virtual void Process(void) = 0;}
> and functions that will go through all of the objects. The functions all
> take a ProcessAll pointer. Now, lets say I want to perform a validation
> check. I have a class that will validate 1 collection point. I combine
> that class with ProcessAll class and pass that pointer to the function the
> goes through all of the collection points. Now, lets say I want to estimate
> missing data. I have a class that will estimate for 1 collection point. I
> combine that class with ProcessAll class and pass that pointer to the
> function that goes through all the collection points. The power of this is
> incredable. In the combined class, I have to define the Process function
> and tell it to pass execution up to the base class. I think it would be
> nice if I could just pass it up without naming the base class -- hince this
> suggestion. But if I have no way of telling it to ignore one of the base
> classes, I have an ambiguity and can't use the method. The thing is, there
> is never a definition for the pure virtual function!
Of course, in reality, there ARE implementations of pure virtual functions.
I believe that in your example inheritance is not the best solution to the
problem anyway. Instead I would suggest a design that does the same thing
and is *MUCH* more extensible, and doesn't have the problem of having to
resolve around pure virtual functions. I'll explain what I mean, but for
reference, if you haven't already read it, pick up a copy of "Design
Patterns: Elements of Re-usable Object Oriented Software" by Erich Gamma,
Richard Helm, Ralph Johnson, and John Vlissades (ISBN: 0-201-63361-2). No,
I have no vested interest in the book. It's just good stuff.
First let me show why I think inheritance isn't the best answer here, and
then I'll show an alternative.
You have a ProcessAll base class and, right now, two simple subclasses, and
one combined class:
Validate ProcessAll Estimate
| | |
------- ------------------ ---------
| | | |
ValidateAll EstimateAll
| |
------------------
|
ValidateAndEstimateAll
Let's assume that you've resolved the issues of calling the inherited
function for a moment. Before going any further, the fact that Validate,
ProcessAll and Estimate have a common interface element (a Process() member
function), suggests that maybe they should have a common base class...
Perhaps Processable? It isn't always the case that the same function
signature implies a common interface, but it's one of those red flags that
should warrant investigation. If you add a common the base class, you have
this:
Processable
|
-------------------------------------
| | |
Validate ProcessAll Estimate
| | |
------- ------------------ ---------
| | | |
ValidateAll EstimateAll
| |
------------------
|
ValidateAndEstimateAll
Anyway, whether you make this change or not is irrelevant to the basic
problem. Suppose you want to add another subclass of ProcessAll. Suppose
you have a class Twiddle, and so you derive a class TwiddleAll in the same
way you did ValidateAll and EstimateAll. Then you discover that you'd like
to sometimes combine Twiddling with Validating, and sometimes with
Estimating and sometimes with both, so now following the pattern of this
design you now have four new classes: TwiddleAll, ValidateAndTwiddle,
EstimateAndTwiddle and ValidateEstimateAndTwiddle. Apart from the fact that
you've now got a seriously awkward naming scheme ;-) this is probably more
classes than you originally intended, but it's not too bad yet.
But suddenly your customers demand that they should sometimes be able to
Frobnicate their objects too. In addition to the new classes from adding
Twiddle, by adding Frobnicate and FrobnicateAll you now have seven more
classes to add (you can work out the combinations yourself). In fact this
grows exponentially. When you have n such simple classes, you have 2^n-1
combinations of those classes. It is easy for this to get seriously out of
control, and even for small values of n, it can a maintenance nightmare.
Either that, or you have to add the restriction that you can only do one of
the operations at a time.
Instead, I'd use a combination of design patterns. In particular the
patterns I'm looking at for this problem are Visitor and Composition.
Internally you'd probably also use the Iterator pattern, but that would
probably come from the standard library. We can even keep the same basic
interface, if you like. The difference here is that ProcessAll becomes a
concrete class (although instead you could derive a concrete class from it
for these purposes).
Processable
|
-------------------------------------
| | |
Validate ProcessAll Estimate
In this case, Validate and Estimate are visitor classes, that is, they will
visit (be applied to) each object of a given structure, but they don't do
any iteration. They are essentially similar to your original Validate and
Estimate classes. ProcessAll (or a subclass) adds a member function
AddVisitor(Processable* inProcessor), which adds a new visitor to an
internally kept list of visitors. When you call ProcessAll's Process()
member function, it iterates through all of the objects it's supposed to
operate on, applying each of the visitors in it's visitor list to each
object in turn.
This design accomplishes the same thing you were trying to accomplish
through inheritance, but has several advantages:
1) When you add a new type of visitor, you add just one class to
your hierarchy. That means less support code, and fewer bugs.
2) You can alter the contents of any ProcessAll instance at
runtime. That is, you can, at runtime, add or remove visitors
as needed by your program.
3) You can have multiple ProcessAll schemes... you can keep
ProcessAll as abstract, and have LinearProcessAll, which
applies the visitor to every object in turn. You can then add
a RandomSampleProcessAll, which applies the visitors to a
random subset of all the objects. This could be useful to
doing a very fast rough estimate by doing calculations on a
sample of objects, and then extrapolating the result for all
the objects. If processing in a particular order makes a
difference sometimes, you can have ProcessAll subclasses that
do it in specific orders. This totally separates the logic of
what to do from deciding which objects to do it to, allowing you
to modify each independently, and then combine them at runtime.
(FYI, doing could use the Strategy pattern to the design)
4) It doesn't rely on an incestuous inheritance scheme, and
thereby totally eliminates the need to resolve around pure
virtual functions, and depending on the specifics of the visitor
classes, for this task, it might eliminate the need to call
inherited functions altogether.
In the solution I just gave, the patterns were determined simply by breaking
the problem down in to smaller parts. You needed to do something to each
object in your system. That's the description of the Visitor pattern. You
needed a way to combine behaviors. That's the description of the
Composition pattern. In #3 I suggested being able to change the way you go
through all the objects... ie, change how something is done. That's the
description of the Strategy pattern.
There are other solutions, but the point is that a different design avoided
the nasty inheritance problems you were seeking work around via language
extesions, not to mention adding other benefits.
> My experience with the addition of the keyword true makes me philosophically
> opposed to adding keywords. I see it as no different than calling a pointer
> to a member function -- the "this->*Function()" syntax. In this case,
> nothing other than the member functions should be allowed to call the base
> functions seems a reasonable restrition. So if "this->Foo()" calls Foo() as
> inherited then "this-->Foo()" would be telling the compiler to move up one
> level (ie "--" suggests subtracting one level) and call a function. This is
> not an idea that I have a particularly strong attachment to; I just thought
> others might see merit in it.
Personally, I've never had situation where the introduction of a keyword
posed any problems that weren't easily cured with mass search and replace
and a recompile.
My reason, in general, for preferring to avoid adding keywords it is to keep
the core language an minimal as possible... no, that's not right. To keep
it as minimal as reasonable. If it were truly minimal, you'd have APL. If
you add keywords for every little thing, you end up with COBOL. IMHO, both
are horrid languages. I'm in favor of adding a keyword where VERY
significant gains can be had from it. It should be something that would be
very helpful any programmer using a reasonable subset of the features of the
language, and it is a decision that should not be taken lightly. I just
think that "inherited" as a scope specifier to access inherited members
meets such a common need that it is worth adding.
> I was thinking more of the company with the largest market share that has
> been known to disable new features by default because it would break there
> own code. Personally, I think an acceptable compromise for them is to
> accept the old coding and issue an "obsolete code" warning. Since I don't
> use that compiler, it is no big deal for me.
I don't use *that* compiler either :-)
I suppose a compiler *could* give you a warning if you were calling an
inherited function by explicitly specifying the scope, but I think it would
be wrong to do so. Explicit scope specifiers would still be completely
legal. A compiler setting to give such warning might not be a bad thing
though, I'm just not sure it's appropriate for a default.
> The syntax I advocate is specific to the this pointer. "this-->Foo()" would
> be the only usage allowed. Something like
> baseclass *bc = this;
> bc-->Foo();
> would be disallowed as would "((baseclass *)this)->Foo()".
Actually, if Foo() is a virtual function both in this and in the base class,
then all of the following have exactly the same result:
BaseClass* basePtr = this; // Case 1
basePtr->Foo();
((BaseClass*)this)->Foo(); // Case 2 - Argh C-style typecast!
static_cast<BaseClass*>(this)->Foo(); // Case 3 - That's better
this->Foo(); // Case 4 - why did we do all the above?
None of which have the same result as your proposed "this-->Foo();", unless
of course, "this" doesn't have it's own implementation of Foo(), but relies
on it's inherited one.
Anyway, you can't stop people from doing this:
ThisClass* ptr = this;
BaseClass* basePtr = ptr;
Even if you eliminated the practice of direct conversion of "this", it's a
simple enough mechanism to defeat.
> Actually, it is an infinite loop. Since it is a loop that consumes a finite
> resource, it is true that it will eventually crash. But it still meets the
> definition of an infinite loop since ordinary execution will never result in
> the loop exiting.
This is splitting hairs, but they are both infinite loops if to you
iteration and recursion are the same. For most programmers, they are
different, hence the difference between a recursive approach to solving a
problem and an iterative approach. Totally academic point though.
Chip Jarred
---
[ 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 ]
Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 2000/08/23 Raw View
"Chip Jarred" <nospam@nospam.com> wrote in message
news:B5C7B3E8.7D15%nospam@nospam.com...
> in article 8nmbkr$1bc6$1@news.hal-pc.org, Greg Brewer at
> nospam.greg@brewer.net wrote on 8/21/00 3:45 PM:
> Are you advocating that pure virtual functions be forbidden from being
> implemented (or that they should cause warnings)? Or are you recommending
> some additional syntax that would indicate that a given pure virtual
> functions does not have a implementation. If the former, I have to
strongly
> disagree... pure virtual means only that subclasses must implement it, and
> in now way should imply whether or not the base class implements it. If
the
> latter, well, I don't have a problem with the concept having some sort of
> "not implemented" specifier.
Definitely the later. As I previously stated, syntax such as "virtual int
Foo(void) =0{};" would be used it mark a pure virtual function.
>
> Personally, I think that if a pure virtual function is causing ambiguity
> between something like "inherited::foo()" (or "this-->foo()", though I
still
> don't like that approach)
then lets drop it and stick with inherited!
> , the situation is not any clearer than if a
> non-pure virtual function caused the ambiguity. This is a case where you
> bite the bullet and explicitly specify a base class, just as you do now.
My
> reason for this are simple. Multiple inheritance is a great feature, but
it
> also has a dark side. Complexity can grow very fast, and so when using
> multiple inheritance, I really want very, very simple resolution rules so
> that problems inherent in multiple inheritance are not complicated further
> by compiler resolution rules. I compare our rules here
>
> Your proposed rule: ambiguity occurs if there is more than one
> inheritance path that can be matched to the "inherited::" member field or
> member function access, excepting the case of member functions, if all but
> one of those inheritance paths match to pure virtual member functions.
No, my rules would be more like
1) pure virtual functions may be marked as unimplemented by placing
braces ("{}") after the pure virtual
marker. If an implementation of a member function is present then an
exception occurs.
2) in a member function, function calls prefixed with "inherited::" will
search the inheritance path for a
matching member field excluding pure virtual functions marked as
unimplemented. Ambiguity occurs if more
than one exact match is found or when an no exact match is found and more
than one inexact match is found.
The search specifically excludes pure virtuals marked as unimplemented.
Also, if one branch defines foo(int) while another branch defines foo(long)
and the call is inherited::foo(int) then no ambiguity exists. On the other
hand, a call to inherited::foo(short) is ambiguous since both are inexact
matches.
>
> My proposed rule: ambiguity occurs if there is more than one
> inheritance path that can be matched to the "inherited::" member field or
> member function access.
I skipped the rest of your posting. The next couple of paragraphs seemed to
assume the first of the two alternatives you gave in paragraph 1. I find it
reasonable to want to call a pure virtual function only if that function may
be implemented. When the function is precluded from having a definition
then it is unreasonable to want to call it.
Greg
---
[ 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 ]
Author: llewelly.@@edevnull.dot.com
Date: 2000/08/30 Raw View
xnews-public@greebo.orion.nl (Martijn Lievaart) writes:
[snip]
> On a side note, does anyone really use the "feature" of defining a body for
> a pure virtual function? I personally would not mind this "feature" to be
> scrapped.
I have used it. Note that removing this feature would make pure
virtual desturctors effectively ill-formed.
> You can always use a different name for your function
> (foo_default()?).
This trick will not work for pure virtual destructors.
[snip]
---
[ 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 ]
Author: alan_griffiths@my-deja.com
Date: 2000/08/30 Raw View
In article <8F9F78A99youdontwannaknow@194.134.193.17>,
xnews-public@greebo.orion.nl (Martijn Lievaart) wrote:
>
> On a side note, does anyone really use the "feature" of defining a
body for
> a pure virtual function?
Yes - probably more often than I use multiple inheritance of
implementation. (So it is more important to me than the 'inherited'
keyword being discussed.)
(And I don't just mean destructors.)
> I personally would not mind this "feature" to be
> scrapped.
You are entitled to your opinion - just don't break my code (or offer to
fix it for free and pay for the QA that results).
> You can always use a different name for your function
> (foo_default()?).
Not always. The reason - in some cases I'm using the change in dynamic
type of the object during construction vs after construction to dispatch
to initialisation vs operational code. (Think 'visitor pattern' applied
at both life stages.)
--
Alan Griffiths (alan.griffiths@experian.com, +44 115 934 4517)
Senior Systems Consultant, Experian
C++ links: http://www.accu.org/ http://www.boost.org/
C++ links: http://www.octopull.demon.co.uk/arglib/
Sent via Deja.com http://www.deja.com/
Before you buy.
---
[ 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 ]
Author: xnews-public@greebo.orion.nl (Martijn Lievaart)
Date: Thu, 31 Aug 2000 16:03:41 GMT Raw View
llewelly.@@edevnull.dot.com wrote in
<m2itsjnzu8.fsf@brownie.frogger.foobar.snot>:
>xnews-public@greebo.orion.nl (Martijn Lievaart) writes:
>
>[snip]
>
>> On a side note, does anyone really use the "feature" of defining a
>> body for a pure virtual function? I personally would not mind this
>> "feature" to be scrapped.
>
>I have used it. Note that removing this feature would make pure
> virtual desturctors effectively ill-formed.
>
Ithought about that 5 minutes after sending the post. But destructors are
special anyhow, so I wouldn't mind making an exception. OTOH, destructors
and how they are invoked seem to be hard on beginners anyhow and this would
make it even more confusing. I personally still wouldn't mind.
M4
---
[ 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 ]
Author: xnews-public@greebo.orion.nl (Martijn Lievaart)
Date: Thu, 31 Aug 2000 17:02:29 GMT Raw View
rogerb@imagebuilder.com (Roger Bonzer) wrote in
<300820001105416283%rogerb@imagebuilder.com>:
>In article <8F9F78A99youdontwannaknow@194.134.193.17>, Martijn Lievaart
><xnews-public@greebo.orion.nl> wrote:
>
>>
>> I would love to see this feature, exactly because of the maintenace
>> nightmares it saves. I have no opinion (yet) on the issue of the
>> ambigious foo(). On one hand, the solution proposed by Greg doesn't
>> "feel" right, but that has mainly to do with the syntax he proposes.
>> OTOH, this ambiguity will arise in practice.
>
>How about something like this:
>
>inherited::foo names the foo in any of the parent classes. if a valid
>match is found in more than one parent class, it is a syntax error.
>
>inherited<parentX>::foo names the foo in parent class parentX. if
>parentX is not an immediate parent class, or if parentX is an immediate
>parent class more than once, or if a valid match is not found in
>parentX, it is a syntax error.
>
I like it. This does exactly what I want, it avoids errors.
M4
---
[ 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 ]
Author: llewelly.@@edevnull.dot.com
Date: Thu, 31 Aug 2000 17:12:13 GMT Raw View
Roger Bonzer <rogerb@imagebuilder.com> writes:
> In article <8F9F78A99youdontwannaknow@194.134.193.17>, Martijn Lievaart
> <xnews-public@greebo.orion.nl> wrote:
>
> >
> > I would love to see this feature, exactly because of the maintenace
> > nightmares it saves. I have no opinion (yet) on the issue of the ambigious
> > foo(). On one hand, the solution proposed by Greg doesn't "feel" right, but
> > that has mainly to do with the syntax he proposes. OTOH, this ambiguity
> > will arise in practice.
>
> How about something like this:
>
> inherited::foo names the foo in any of the parent classes. if a valid
> match is found in more than one parent class, it is a syntax error.
I can see reason to have 'inherited::foo'. (I think the need is small
but real.)
>
> inherited<parentX>::foo names the foo in parent class parentX. if
> parentX is not an immediate parent class, or if parentX is an immediate
> parent class more than once, or if a valid match is not found in
> parentX, it is a syntax error.
But why does 'inherited<parentX>::' keep popping up when we already have
'parentX::' ?
Does 'inherited<parentX>::' fill any need at all that is not filled by
the qualification syntax ('parentX::') that we already have?
---
[ 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 ]
Author: Roger Bonzer <rogerb@imagebuilder.com>
Date: Thu, 31 Aug 2000 18:24:58 GMT Raw View
In article <m2ya1dmn5w.fsf@brownie.frogger.foobar.snot>,
<llewelly.@@edevnull.dot.com> wrote:
> >
> > How about something like this:
> >
> > inherited::foo names the foo in any of the parent classes. if a valid
> > match is found in more than one parent class, it is a syntax error.
> >
> > inherited<parentX>::foo names the foo in parent class parentX. if
> > parentX is not an immediate parent class, or if parentX is an immediate
> > parent class more than once, or if a valid match is not found in
> > parentX, it is a syntax error.
>
> But why does 'inherited<parentX>::' keep popping up when we already have
> 'parentX::' ?
>
> Does 'inherited<parentX>::' fill any need at all that is not filled by
> the qualification syntax ('parentX::') that we already have?
Yes, in the exact same way that 'static_cast<Foo *>(GetBarPtr())' fills
a need not filled by the '(Foo *)GetBarPtr()'. If you change the
derivation of Bar so that it *isn't* derived from Foo, the
static_cast<> will be a syntax error, while the C-style cast will
likely be an annoying bug you might detect at runtime.
For the inherited<parentX> case, if you change the derivation so that
parentX is really a "grandparent", inherited<parentX>:: will be a
syntax error, while parentX:: will likely be an annoying bug you might
detect at runtime.
---
[ 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 ]
Author: "Trevor L. Jackson, III" <fullmoon@aspi.net>
Date: Thu, 31 Aug 2000 20:55:24 GMT Raw View
"llewelly."@@edevnull.dot.com wrote:
> Roger Bonzer <rogerb@imagebuilder.com> writes:
>
> > In article <8F9F78A99youdontwannaknow@194.134.193.17>, Martijn Lievaart
> > <xnews-public@greebo.orion.nl> wrote:
> >
> > >
> > > I would love to see this feature, exactly because of the maintenace
> > > nightmares it saves. I have no opinion (yet) on the issue of the ambigious
> > > foo(). On one hand, the solution proposed by Greg doesn't "feel" right, but
> > > that has mainly to do with the syntax he proposes. OTOH, this ambiguity
> > > will arise in practice.
> >
> > How about something like this:
> >
> > inherited::foo names the foo in any of the parent classes. if a valid
> > match is found in more than one parent class, it is a syntax error.
>
> I can see reason to have 'inherited::foo'. (I think the need is small
> but real.)
>
> >
> > inherited<parentX>::foo names the foo in parent class parentX. if
> > parentX is not an immediate parent class, or if parentX is an immediate
> > parent class more than once, or if a valid match is not found in
> > parentX, it is a syntax error.
>
> But why does 'inherited<parentX>::' keep popping up when we already have
> 'parentX::' ?
>
> Does 'inherited<parentX>::' fill any need at all that is not filled by
> the qualification syntax ('parentX::') that we already have?
I believe it does (although it's a nasty abuse of the template syntax). The cases
I view as justifying the additional keyword are based on the observation that the
scopes available at any particular point have three useful subsets, those being
current and any ancestor, current-only, and ancestor only. In the case of multiple
inheritance we have the additional need to select a particular line of descent.
The current-and-any-ancestor (CAAA) is the natural default that applies to an
unqualified name. Currently-only (CO) is the existing interpretation of qualified
names. Ancestor-only (AO) is particularly useful when an overriding function calls
the overridden inherited version.
The CAAA and AO contexts are of limited use for handling multiple inheritance. The
CO context is useful but, because it mandates an explicit reference to each scope
along the line of descent, it incorporates too much class lattice information into
the scope construct. So some kind of non-specific ancestor selector is necessary.
The selector is not only necessary in the case that the current context has
multiple parents, but also in the case that an ancestor has multiple parents.
Given a single keyword (my preference is "via" rather than "inherited") we can
interpret the keyword in several ways. The simplest, inherited::member, supports
the AO context. By extension of Ancestor::member to Ancestor::inherited::member we
can obtain the AO context for Ancestor. The reason this ordering is sensible is
that it connotes that the desired scope is the AO subset of the scope Ancestor.
The sequence inherited::Ancestor::member can be interpreted to be inherited via
Ancestor. I.e., it excludes all lines of descent that do not pass through
Ancestor.
With these interpretations even multiply inherited ancestors can be resolved with
the bare minimum of inheritance hints.
---
[ 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 ]
Author: David R Tribble <david@tribble.com>
Date: Fri, 1 Sep 2000 12:34:48 GMT Raw View
Chip Jarred wrote:
>[directed to Greg Brewer]
> Your proposed rule: ambiguity occurs if there is more than one
> inheritance path that can be matched to the "inherited::" member field
> or member function access, excepting the case of member functions, if
> all but one of those inheritance paths match to pure virtual member
> functions.
>
> My proposed rule: ambiguity occurs if there is more than one
> inheritance path that can be matched to the "inherited::" member field
> or member function access.
If we limit the multiple inheritance of a class to several "interface"
bases classes and only one "value" class, we could simplify the
meaning of 'inherited' (or 'super') to refer only to the "value" base
class. (This is similar to the Java inheritance model, where a class
can implement/inherit multiple interfaces, but only one value base
class.)
// an interface class ---------------------------------
class Color
{
public:
// contains only virtual functions
virtual int getColor();
virtual void setColor(int c);
};
// an interface class ---------------------------------
class Shape
{
public:
// contains only virtual functions
virtual int getShape();
virtual void setShape(int c);
};
// a value class --------------------------------------
class Position
{
public:
// contains virtual functions
virtual int getX();
virtual int getY();
...
private:
// also contains data members
int x;
int y;
};
// a derived class ------------------------------------
class Thingy:
public Position, Color, Shape
{
protected:
typedef Position inherited;
// denotes the single value base class only
public:
// can override inherited base functions
virtual int getX()
{
// may use 'inherited::' here
return inherited::getX() / 2;
}
virtual int getColor()
{
// cannot use 'inherited::' here
...
}
};
In other words, we limit 'inherited' to refer to a single specific
base class. If only one of the base classes actually contains data
members (i.e., has "value"), then that is the logical choice to be
the 'inherited' class.
--
David R. Tribble, mailto:david@tribble.com, http://david.tribble.com
---
[ 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 ]
Author: llewelly.@@edevnull.dot.com
Date: Fri, 1 Sep 2000 17:08:17 GMT Raw View
xnews-public@greebo.orion.nl (Martijn Lievaart) writes:
> llewelly.@@edevnull.dot.com wrote in
> <m2itsjnzu8.fsf@brownie.frogger.foobar.snot>:
>
> >xnews-public@greebo.orion.nl (Martijn Lievaart) writes:
> >
> >[snip]
> >
> >> On a side note, does anyone really use the "feature" of defining a
> >> body for a pure virtual function? I personally would not mind this
> >> "feature" to be scrapped.
> >
> >I have used it. Note that removing this feature would make pure
> > virtual desturctors effectively ill-formed.
> >
>
> Ithought about that 5 minutes after sending the post. But destructors are
> special anyhow, so I wouldn't mind making an exception. OTOH, destructors
> and how they are invoked seem to be hard on beginners anyhow and this would
> make it even more confusing. I personally still wouldn't mind.
Did you read the other replies to your suggestion? There are C++
programmers who rely on this feature.
[snip]
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: Sat, 2 Sep 2000 04:02:12 GMT Raw View
in article 8F9F78A99youdontwannaknow@194.134.193.17, Martijn Lievaart at
xnews-public@greebo.orion.nl wrote on 8/29/00 2:38 PM:
> On a side note, does anyone really use the "feature" of defining a body for
> a pure virtual function? I personally would not mind this "feature" to be
> scrapped. You can always use a different name for your function
> (foo_default()?).
It's not likely to be scrapped, but it wouldn't break my heart either...
there is another way to do it. You simply provide your implementation in
another protected member function, and if subclasses need it, they call that
other member function. Still, I do on occasion use the ability to provide
an implementation for a pure virtual.
> Another issue is that adding a new keyword is sure to break a lot of
> sources. Maybe we could use something else? Like a new token? I thought
> about "^::foo()" but that can occur legally in a normal program. How about
> "::^foo()" ?
I too have tried to think of ways to do it too, without adding a keyword,
but every solution I could think of, and every one I have seen so far,
including yours, share a common problem, which is that they don't visually
mean what you really intend. Under normal circumstances, "^" means
exclusive-or (although, it can be redefined via operator overloading).
Aside from a completely arbitrary assignment, it doesn't seem have any
relationship with the notion of calling an inherited member function. So
having a new keyword seems appropriate, and worth it. As for it breaking a
"lot" of code... well, of course, I don't know enough about everyone else's
code, but I suggest two things. First, that it wouldn't break very much at
all... how often have you ever seen the a symbol called "inherited" in your
work? I guess it could happen, but to do date I have only seen it in once,
which brings me to the second point: that where it is used in source code,
it probably is being used to accomplish kludgily the very thing that the
keyword is going to do much better. Probably some definitions in various
classes will need to be removed. In most other cases, a simple massive
search and replace would do the trick. On the whole, I think the benefits
are worth the very few headaches it might cause on introduction.
As an aside, I do honestly think that those who use a naming standard which
makes it possible, even likely, to clash with keywords should seriously
reconsider their naming standards. I know that Unix programmers often have
traditions of using all lowercase letters, but most of the libraries that I
have seen usually provide prefixes for library symbols to prevent this very
thing - of course, from their point of view, it's an attempt to prevent name
collision with application code. IMHO, application programmers should do
the same thing. Though I hate Hungarian notation, a lot of Windows
programmers use it, and even those who don't often used mixed case in their
symbols, which prevents this kind of problem. Mac programming also has a
rich tradition of mixed case naming (as a result of its roots in Pascal),
and the vast majority of Mac programmers use either Metrowerks' PowerPlant
or Apple's MacApp for an application framework, both of which have naming
conventions which make it impossible (if followed) to collide with new
keywords. I do think that those who are proposing a new keyword (as I am),
do have to consider very seriously the impact on existing source code, but I
also think programmers need to be responsible about their own coding
standards to avoid headaches of their own... if you name something in a way
that looks like it could be a keyword, then you should probably rename it.
As Dennis Miller says "That's just my opinion. I could be wrong."
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: Sat, 2 Sep 2000 04:04:49 GMT Raw View
in article 300820001105416283%rogerb@imagebuilder.com, Roger Bonzer at
rogerb@imagebuilder.com wrote on 8/30/00 1:19 PM:
> In article <8F9F78A99youdontwannaknow@194.134.193.17>, Martijn Lievaart
> <xnews-public@greebo.orion.nl> wrote:
>
>>
>> I would love to see this feature, exactly because of the maintenace
>> nightmares it saves. I have no opinion (yet) on the issue of the ambigious
>> foo(). On one hand, the solution proposed by Greg doesn't "feel" right, but
>> that has mainly to do with the syntax he proposes. OTOH, this ambiguity
>> will arise in practice.
>
> How about something like this:
>
> inherited::foo names the foo in any of the parent classes. if a valid
> match is found in more than one parent class, it is a syntax error.
I like that... maybe because it's exactly what I'm proposing :-)
> inherited<parentX>::foo names the foo in parent class parentX. if
> parentX is not an immediate parent class, or if parentX is an immediate
> parent class more than once, or if a valid match is not found in
> parentX, it is a syntax error.
This on the other hand, doesn't seem that useful. Why not just use
parentX::foo? After all, just because you say parentX::foo doesn't mean
that foo has to be defined in parentX. It might be defined in one of
parentX's ancestors. parentX::foo just means access the foo that is seen by
parentX.
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: Sat, 2 Sep 2000 22:15:55 GMT Raw View
in article m2itsjnzu8.fsf@brownie.frogger.foobar.snot,
llewelly.@@edevnull.dot.com at llewelly.@@edevnull.dot.com wrote on 8/30/00
1:36 PM:
> xnews-public@greebo.orion.nl (Martijn Lievaart) writes:
>
> [snip]
>
>> On a side note, does anyone really use the "feature" of defining a body for
>> a pure virtual function? I personally would not mind this "feature" to be
>> scrapped.
>
> I have used it. Note that removing this feature would make pure
> virtual desturctors effectively ill-formed.
Good point.
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: Sat, 2 Sep 2000 22:16:12 GMT Raw View
in article 39AEC58A.12E38C43@aspi.net, Trevor L. Jackson, III at
fullmoon@aspi.net wrote on 8/31/00 3:55 PM:
> The current-and-any-ancestor (CAAA) is the natural default that applies to an
> unqualified name. Currently-only (CO) is the existing interpretation of
> qualified
> names. Ancestor-only (AO) is particularly useful when an overriding function
> calls
> the overridden inherited version.
I'm not so sure that "ancestor only" is a good word for this, since in
reality the use of inherited:: would be to call whatever is inherited, even
if not defined in any immediate ancestor. In fact, that's the whole
point... you can insert new class in the hierarchy without breaking the
breaking subclasses, whether or not you redefine the member in question.
> The CAAA and AO contexts are of limited use for handling multiple inheritance.
Actually, given that I think that the most frequent use of multiple
inheritance is that the base classes are mixing-in non-overlapping
interfaces, both CAAA and AO are of prime usefulness (with the possible
exception of inherited overloaded operators). I think the ugly cases mostly
happen as a result of incestuous inheritance trees, and in operators.
> The
> CO context is useful but, because it mandates an explicit reference to each
> scope
> along the line of descent, it incorporates too much class lattice information
> into
> the scope construct. So some kind of non-specific ancestor selector is
> necessary.
> The selector is not only necessary in the case that the current context has
> multiple parents, but also in the case that an ancestor has multiple parents.
>
> Given a single keyword (my preference is "via" rather than "inherited") we can
> interpret the keyword in several ways.
FYI, my reasoning for suggesting "inherited", is not only because it is
descriptive, particularly in the most common case of unambiguous inherited
member access, but also because it is already part of a at least one
implementation of Objective C to solve this very task. Of course, Objective
C, supports only single inheritance, so the variations we're discussing
aren't even an issue for it. "via" seems to make more sense in the case of
ambiguous member access in multiple inheritance than in the more common
unambiguous cases. The *most* important thing to me is the feature, not
specific name of the keyword. I prefer "inherited", but I could get used to
"via".
> The simplest, inherited::member,
> supports
> the AO context. By extension of Ancestor::member to
> Ancestor::inherited::member we
> can obtain the AO context for Ancestor. The reason this ordering is sensible
> is
> that it connotes that the desired scope is the AO subset of the scope
> Ancestor.
This makes sense... I don't immediately see a practical use, but that
doesn't mean there isn't one. In any case, it is a logical extension of
current scoping rules.
> The sequence inherited::Ancestor::member can be interpreted to be inherited
> via
> Ancestor. I.e., it excludes all lines of descent that do not pass through
> Ancestor.
Ancestor::member does exactly the same thing, unless you mean that in the
phrase "inherited::X::member" that X must be an immediate ancestor. In that
case I'll refer you to another of my posts (referring to inherited<X>::
syntax) where I show a case where specifying a base class is still needed,
but restricting X to the immediate base class is not sufficient. If on the
other hand X can be any ancestor then it's the same as X::member.
> With these interpretations even multiply inherited ancestors can be resolved
> with
> the bare minimum of inheritance hints.
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: Sat, 2 Sep 2000 22:16:23 GMT Raw View
in article 39AEA306.3FD6560D@tribble.com, David R Tribble at
david@tribble.com wrote on 9/1/00 7:34 AM:
> Chip Jarred wrote:
>> [directed to Greg Brewer]
>> Your proposed rule: ambiguity occurs if there is more than one
>> inheritance path that can be matched to the "inherited::" member field
>> or member function access, excepting the case of member functions, if
>> all but one of those inheritance paths match to pure virtual member
>> functions.
>>
>> My proposed rule: ambiguity occurs if there is more than one
>> inheritance path that can be matched to the "inherited::" member field
>> or member function access.
>
> If we limit the multiple inheritance of a class to several "interface"
> bases classes and only one "value" class, we could simplify the
> meaning of 'inherited' (or 'super') to refer only to the "value" base
> class. (This is similar to the Java inheritance model, where a class
> can implement/inherit multiple interfaces, but only one value base
> class.)
>
> // an interface class ---------------------------------
> class Color
> {
> public:
> // contains only virtual functions
> virtual int getColor();
> virtual void setColor(int c);
> };
>
> // an interface class ---------------------------------
> class Shape
> {
> public:
> // contains only virtual functions
> virtual int getShape();
> virtual void setShape(int c);
> };
>
> // a value class --------------------------------------
> class Position
> {
> public:
> // contains virtual functions
> virtual int getX();
> virtual int getY();
> ...
> private:
> // also contains data members
> int x;
> int y;
> };
>
> // a derived class ------------------------------------
> class Thingy:
> public Position, Color, Shape
> {
> protected:
> typedef Position inherited;
> // denotes the single value base class only
>
> public:
> // can override inherited base functions
> virtual int getX()
> {
> // may use 'inherited::' here
> return inherited::getX() / 2;
> }
>
> virtual int getColor()
> {
> // cannot use 'inherited::' here
> ...
> }
> };
>
> In other words, we limit 'inherited' to refer to a single specific
> base class. If only one of the base classes actually contains data
> members (i.e., has "value"), then that is the logical choice to be
> the 'inherited' class.
In principle I agree that multiple inheritance cases should largely have a
single realy inheritance, and multiple classes that define additional
interfaces, but I think Java's approach is a little too limiting. It is
often very convenient (possibly necessary, though I can't think of an
example at the moment) to have such "interface" classes which contain state
information to, making them to a limited degree also "value" classes. Still
it is good design to limit this to a minimum. C++ does allow multiple
inheritance just about any type, and I wouldn't want to prevent those who do
use it in ways that I wouldn't necessarily approve of, from using inherited
when it would be unambiguous. I do think that when C++ programmers use
multiple inheritance, we do need to realize that there are potential
complications, and I really think that when inherited:: would be ambiguous,
it is not overly burdensome to require an explicit scope specifier.
As for the use of inherited vs. super, I have already given my opinion in an
earlier post, but the summary is basically this. Most suggestion to use
super is in the Java sense, which is super->member (although in Java it is
actually super.member). My problem with this is not consistent with the
manner that C++ already allows access to inherited members which is via a
scope specifier ie. BaseClass::member. I would prefer that we keep the
syntax consistent, so whatever keyword is proposed, it would only receive my
full support if it were in the form keyword::member. The preference for the
keyword "inherited" in particular, is that it has precedence in at least one
implementation of Objective C, which I feel is more related to C++ than Java
is. Still if the C++ community were to decide that they prefer
super::member instead of inherited::member, it wouldn't break my heart.
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: Sun, 3 Sep 2000 12:28:26 GMT Raw View
in article 8oepui$27v9$1@news.hal-pc.org, Greg Brewer at
nospam.greg@brewer.net wrote on 8/29/00 9:30 AM:
> I agree that the burden is on me to demonstrate a need. I just can't figure
> out how to accomplish this. I commonly use a technique imploying pure
> virtual functions as a stub for some undertermined operation. I find this
> to be a very useful and powerful way. In these instances, there is no
> ambiguity because the pure virtual function is never used; indeed I didn't
> know that a pure virtual function could have an implementation. Now that I
> know, I have suggested a mechanism that solves the problem. Without this
> exception, I cannot use inherited because almost all uses will be ambiguous.
Of course, anyone who sees the value in purely abstract classes needs to use
pure virtual member functions. Most of mine also do not have an
implementation, but I do use that features sometimes. In any case, it would
be very rare in my programming where the introduction of "inherited" would
cause ambiguity, because I try very hard to avoid having multiple paths back
to some common base class (ie... I try to avoid the situation where I'd need
to use virtual base classes). Even so, it is still possible when multiple
base classes, though unrelated, provide some pure virtual function as the
some other base class. This doesn't happen very often when 'normal' member
functions, but it is a lot more likely with overloaded operators. Still in
these cases, I don't mind explicitly specifying the base class. When this
happens, though, it really makes me take a long second look at my design.
> On the other hand, I will probably never use it one way or the other. I am
> slow to upgrade my compilers because I have yet to have an upgrade that
> would compile my existing code. By the time what I want can be canonized
> and implemented, I expect to have made my fortune and retired.
>
> I too would like to hear from some others on the subject.
I hope more will chime in :-)
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: Sun, 3 Sep 2000 12:29:18 GMT Raw View
in article 310820001108082726%rogerb@imagebuilder.com, Roger Bonzer at
rogerb@imagebuilder.com wrote on 8/31/00 1:24 PM:
> In article <m2ya1dmn5w.fsf@brownie.frogger.foobar.snot>,
> <llewelly.@@edevnull.dot.com> wrote:
>
>>>
>>> How about something like this:
>>>
>>> inherited::foo names the foo in any of the parent classes. if a valid
>>> match is found in more than one parent class, it is a syntax error.
>>>
>>> inherited<parentX>::foo names the foo in parent class parentX. if
>>> parentX is not an immediate parent class, or if parentX is an immediate
>>> parent class more than once, or if a valid match is not found in
>>> parentX, it is a syntax error.
>>
>> But why does 'inherited<parentX>::' keep popping up when we already have
>> 'parentX::' ?
>>
>> Does 'inherited<parentX>::' fill any need at all that is not filled by
>> the qualification syntax ('parentX::') that we already have?
>
> Yes, in the exact same way that 'static_cast<Foo *>(GetBarPtr())' fills
> a need not filled by the '(Foo *)GetBarPtr()'. If you change the
> derivation of Bar so that it *isn't* derived from Foo, the
> static_cast<> will be a syntax error, while the C-style cast will
> likely be an annoying bug you might detect at runtime.
>
> For the inherited<parentX> case, if you change the derivation so that
> parentX is really a "grandparent", inherited<parentX>:: will be a
> syntax error, while parentX:: will likely be an annoying bug you might
> detect at runtime.
So if you use inherited<parentX>:: then parentX must be an immediate base
class? On the surface this seems reasonable, but I think there is a problem
hiding somewhere... what if the ambiguity occurs somewhere further up the
hiearchy?
class A1
{
public:
virtual void foo(void) {...};
};
class A2
{
public:
virtual void foo(void) {...};
};
class B : public A1, public A2
{
public:
// doesn't do anything with foo - so ambiguity detected here
};
class C : public B
{
public:
virtual void foo(void)
{
inherited::foo(); // ambiguous
inherited<B>::foo(); // still ambiguous
inherited<A1>::foo(); // can't use inherited to reference
// non-immediate ancestor
A1::foo(); // This works.
}
};
First let me say that I try very hard NOT to write code this way...
actually, it doesn't take much effort to avoid. Just thinking about design
for another 10 minutes (or less) is usually enough to come up with another
solution that doesn't cause this ugliness. Still, demonstrates the kind of
ambiguity we are trying to deal with here.
My point in giving this example is that 1) multiple inheritance can lead to
some ugly stuff, so we need to be careful and use it wisely, and 2) you
cannot define inherited<X>:: in such a way that it sufficiently removes
ambiguity unless you make it functionally the same as X:: I guess you could
say that inherited<X> requires that X be the closest ancestor on an
inheritance path which removes the ambiguity, but now we're getting into a
definition which is more complicated, and I'm not convinced that the
additional complexity is worth the benefits... I believe that inherited::
will take care of the overwhelming majority of bugs and maintenance hassles
where calling an inherited function is required, and I think I'd rather keep
the language as simple as we can for the cases where you do have ambiguity.
I don't have a problem with requiring the use of X:: when inherited:: is
ambiguous.
Having said that, given a choice between accepting the inherited<X>:: form,
or not having inherited at all, I would rather accept the more complicated
version than continue to do without it altogether. So I'm not *that*
against it.
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/13 Raw View
in article 8mrjhc$2hm1$1@news.hal-pc.org, Greg Brewer at
nospam.greg@brewer.net wrote on 8/9/00 12:08 PM:
> According tot he columnist though, the big reason it isn't present is
> because of multiple inheritence could make the call ambiguous. I think that
> such a mechanism should be available and if the call is ambiguous either
> issue an error message or search in order of inclusion. I lean toward the
> error message approach if the function exists in non-pure virtual form in
> more than on base class. In other words, the following should not be
> ambigous.
> class A
> {
> public:
> virtual void Foo(void) = 0;
> };
> class B
> {
> public:
> void Foo(void) {}
> };
> class AB : public A, public B
> {
> public:
> virtual void Foo(void) {this-->Foo(); or super(this)->Foo();}
> };
Actually it is ambiguous. The fact that a member function is pure virtual
does not mean that it is not implemented in the class that defines it as
pure virtual. Rather it simply means that sub-classes must implement it.
You can define a member function, which is pure virtual, and then implement
it to give it a default implementation which can be inherited and used in
subclasses. The catch is that the subclasses must MUST implement their own
version and explicitly call the inherited version.
class A
{
public:
virtual void Foo(void) = 0 { /* default implementation here */ }
};
class C : public A
{
public:
virtual void Foo(void) { A::Foo()); /* This is legal */ }
};
So in the multiple inheritance case you described, you still have an
ambiguous member function call.
Regarding the whole concept of calling the inherited version of a member
function, I agree that it would be useful. I like the way that it was done
in Think C on the Macintosh which implemented C with objects. It only
supported single inheritance, but it included the keyword inherited which
was used as scope specifier:
class C : public A
{
public:
virtual void Foo(void) { inherited::Foo(); }
};
In the case of ambiguity due to multiple base classes, I think the compiler
should generate an error and force you to use a more explicit scope
specifier.
If something like Java's super were added, I wouldn't want to have to pass
"this" to it. If it were a keyword, there's no reason you couldn't just do
this:
super->Foo();
Personally I prefer "inherited" over "super" because I think it would be
more syntactically faithful to C++'s current syntax. Right now, when you
want to call an inherited member function, you do it by specifying it's
scope:
BaseClass::Foo();
Since any keyword that would do the same thing, but without specifying the
specific class, is still effectively performing the same task, I think it
should have the same syntax:
inherited::Foo();
Like Dennis Miller says, "that's just my opinion. I could be wrong."
Chip Jarred
---
[ 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 ]
Author: dwalker07@snet.net.invalid (Daryle Walker)
Date: 2000/08/14 Raw View
Chip Jarred <nospam@nospam.com> wrote:
> class C : public A
> {
> public:
> virtual void Foo(void) { inherited::Foo(); }
> };
>
> In the case of ambiguity due to multiple base classes, I think the compiler
> should generate an error and force you to use a more explicit scope
> specifier.
Maybe we can use a puesdo-template syntax for multiple inheiritance.
class C
: public A, public B
{
public:
virtual void Foo(void) { inherited<A>::Foo(); }
virtual void Bar(void) { inherited<B>::Bar(); }
};
For single inheiritance, we could allow the template part to be dropped,
like Chip's proposal.
The base class in the template part could be allowed to be an indirect
base class, provided that class appears unambigously (exactly one of the
direct base classes uses it [directly or indirectly] or multiple
instances are collasped into one with virtual inheiritance).
--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/14 Raw View
in article B5B9E186.7963%nospam@nospam.com, Chip Jarred at nospam@nospam.com
wrote on 8/13/00 3:08 PM:
> Fortunately, the more common mistake (of changing B's base
> class, but forgetting to make the corresponding change in the typedef) would
> be caught by the compiler.
Actually, as I think about this, it would only sometimes be caught by the
compiler. On the whole, implementing something like "inherited" using
current language constructs leaves a lot of room for user error that could
be detected or corrected in a keyword. Does that mean that it *should* be
added as a keyword? Well, I have mixed feelings about that. On the one
hand, I would prefer to keep the core language as minimal as possible. On
the other hand, I would like it to have features which help write correct
programs in a way which is harder to break through maintenance, as long as
the the addition of that feature doesn't adversely affect optimizations.
Since something like "inherited" could be entirely worked out at compile
time, I don't think it's addition would have a negative impact on object
code generation.
Chip Jarred
---
[ 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 ]
Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 2000/08/14 Raw View
That is interesting; I just learned something new. Still I would prefer the
following to not be ambiguous.
class A
{
public:
virtual int Foo(void) = 0;
};
class B
{
public:
int Foo(void) {return 99;}
};
class AB : public A, public B
{
public:
int Foo(void) {return inherited::Foo();}
};
// or how about
template <class X>
class AX : public A, public X
{
public:
int Foo(void) {return inherited::Foo();}
};
// ....
class AX<B> ab;
};
I think I still prefer not to introduce another keyword.
Greg
---
[ 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 ]
Author: John Kewley <kewley@cscs.ch>
Date: 2000/08/14 Raw View
Chip Jarred wrote:
> ...
> Personally I prefer "inherited" over "super" because I think it would be
> more syntactically faithful to C++'s current syntax.
I think for historical reasons the "static" keyword would be more
appropriate; why should we use new keywords when old ones will do.
After all, its use for "file static" is currently being depracated,
so it is free to be used in more interesting ways
;-)
Seriously, the use of the keyword "inherited" implies that it is one
of your ancestor classes that you are calling, wheras
super or base implies parent.
Cheers,
--
John M. Kewley Tel: +41 (0) 91 610 8248
Senior Scientist Fax: +41 (0) 91 610 8282
Swiss Center for Scientific Computing mailto:John.Kewley@cscs.ch
Via Cantonale, CH-6928 Manno, Switzerland http://www.cscs.ch/%7Ekewley/
---
[ 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 ]
Author: Steven King <sxking@uswest.net>
Date: 2000/08/15 Raw View
Chip Jarred wrote:
>
> Actually it is ambiguous. The fact that a member function is pure virtual
> does not mean that it is not implemented in the class that defines it as
> pure virtual. Rather it simply means that sub-classes must implement it.
> You can define a member function, which is pure virtual, and then implement
> it to give it a default implementation which can be inherited and used in
> subclasses. The catch is that the subclasses must MUST implement their own
> version and explicitly call the inherited version.
>
> class A
> {
> public:
> virtual void Foo(void) = 0 { /* default implementation here */ }
> };
>
You can not do it this way. Section 10.4/2 notes: "a function
declaration cannot provide both a pure-specifier and a definition" and
gives as an example:
struct C {
virtual void f() { }=0; // ill-formed
};
instead one must define the pure virtual function outside of the body
of the class. ie:
class A
{
public:
virtual void Foo(void) = 0;
};
void
A::Foo ()
{
/* default implementation here */
}
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/17 Raw View
in article 1efc0zm.ayiad6pf7w35N%dwalker07@snet.net.invalid, Daryle Walker
at dwalker07@snet.net.invalid wrote on 8/14/00 10:21 AM:
> Chip Jarred <nospam@nospam.com> wrote:
>
>> class C : public A
>> {
>> public:
>> virtual void Foo(void) { inherited::Foo(); }
>> };
>>
>> In the case of ambiguity due to multiple base classes, I think the compiler
>> should generate an error and force you to use a more explicit scope
>> specifier.
>
> Maybe we can use a puesdo-template syntax for multiple inheiritance.
>
> class C
> : public A, public B
> {
> public:
> virtual void Foo(void) { inherited<A>::Foo(); }
> virtual void Bar(void) { inherited<B>::Bar(); }
> };
>
> For single inheiritance, we could allow the template part to be dropped,
> like Chip's proposal.
>
> The base class in the template part could be allowed to be an indirect
> base class, provided that class appears unambigously (exactly one of the
> direct base classes uses it [directly or indirectly] or multiple
> instances are collasped into one with virtual inheiritance).
>
Perhaps, but I wouldn't like using template syntax when it's not a template.
In this case, you have to specify a specific base class anyway so what
benefit does "inherited<B>::Foo()" have over simply writing "B::Foo()"?
Basically, in the case of an ambiguous call due to a member function (or
member field, for that matter) being defined in multiple base classes, I see
no way to resolve the ambiguity without somehow explicitly specifying one of
the base classes. That being the case, C++'s current syntax handles the
case as well as any proposal I can think of. Instead, just reserve
"inherited" for unambiguous inherited member access.
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/17 Raw View
in article 3997CF89.1E3037B4@cscs.ch, John Kewley at kewley@cscs.ch wrote on
8/14/00 11:11 AM:
> Chip Jarred wrote:
>> ...
>> Personally I prefer "inherited" over "super" because I think it would be
>> more syntactically faithful to C++'s current syntax.
>
> I think for historical reasons the "static" keyword would be more
> appropriate; why should we use new keywords when old ones will do.
> After all, its use for "file static" is currently being depracated,
> so it is free to be used in more interesting ways
> ;-)
But "static" doesn't in anyway mean the same thing. I grant you that
sometimes C++ keywords are highly specialized forms of their common English
usage, but there's always some relationship. I don't see the relationship
here.
> Seriously, the use of the keyword "inherited" implies that it is one
> of your ancestor classes that you are calling, wheras
> super or base implies parent.
Yes, and that's exactly the point. You're calling the inherited version,
not the version overridden in your class. It seeks up the inheritance chain
(along all branches in the case of multiple inheritance). If defined in
multiple branches of the ancestor tree, the call is ambiguous and should
generate an error. As long as it's not ambiguous, it calls the version
that would be called if you hadn't over-ridden in your own class. I see no
reason to require that it is defined in the immediate base class. Do make
such a requirement would actually break the intent. The idea is to provide
a means of calling inherited functions, without explicitly specifying the
base class. Suppose you have this situation:
class Base
{
...
virtual void Foo(void);
...
};
class Derived : public Base
{
...
virtual void Foo(void)
{
...
inherited::Foo();
...
}
...
};
Now suppose you add a class to this heirarchy
class Intermediate : public Base
{
// Does not override Foo
}
class Derived : public Intermediate
{
// Same as before
}
Why should doing this break Derived's Foo? It shouldn't. The fact that you
have introduced an intermediate class between the two that does not
explicitly define Foo is irrelevant, and Derived doesn't really care.
Through inheritance, Foo is part of Intermediate's interface too, and Foo
just wants to call that, and it really doesn't care whether or not
Intermediate has overridden it. If you really want to explicitly specify
which class you want to call, you can always use an explicit scope specifier
(like Base::Foo()). Some of our standards gurus should chime in here, but I
don't think even this guarantees that Foo is actually defined in Base... it
might be defined in one of Base's ancestors. Believe Base::Foo() just says
to use whatever Foo is available in Base, whether Base has it through
inheritance or by explicit definitio.
Chip Jarred
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/17 Raw View
in article 8n904f$a6d$1@news.hal-pc.org, Greg Brewer at
nospam.greg@brewer.net wrote on 8/14/00 11:12 AM:
> That is interesting; I just learned something new. Still I would prefer the
> following to not be ambiguous.
> class A
> {
> public:
> virtual int Foo(void) = 0;
> };
> class B
> {
> public:
> int Foo(void) {return 99;}
> };
> class AB : public A, public B
> {
> public:
> int Foo(void) {return inherited::Foo();}
> };
> // or how about
> template <class X>
> class AX : public A, public X
> {
> public:
> int Foo(void) {return inherited::Foo();}
> };
> // ....
> class AX<B> ab;
> };
The problem with these examples is that the compiler cannot know whether
class A implemented it's own version of Foo() (remember, that pure virtual
just means that sub-classes must implement it, not that the class that
declares it pure virtual does not). Although it is common practice to put
the implementation of all of the member functions in their own source file
(or the in the header that defines the class for inline or templates), this
is not required. There is nothing about C++ that says that you cannot
define your implementation of class A in multiple source files. This means
that the only time that your development system will know if class A
implements Foo() is at link time, not at compile time. Even if the linker
is smart enough to know that it needs to somehow handle this, the compiler
doesn't really know what linker you will use, so it has no choice but to
assume that class A might have an implementation of Foo somewhere that it
has not seen. Therefore the call to Foo() MUST be ambiguous.
> I think I still prefer not to introduce another keyword.
>
> Greg
I don't like adding keywords willy nilly either. There is another thread
where someone suggested a keyword "invariant" for "for" loops. I'm against
that suggestion. The thing here is that calling inherited member functions
is a VERY common idiom in any object oriented language (not just C++). It's
so common, and such an integral part of OO design that I strongly believe
that programmers would benefit from not having to explicitly specify the
name of the base class when calling an inherited function which is otherwise
unambiguous.
Chip Jarred
---
[ 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 ]
Author: Heinz Huber <Heinz.Huber@elbanet.co.at>
Date: 2000/08/17 Raw View
Greg Brewer wrote:
[snip proposal and motivation for super]
> According tot he columnist though, the big reason it isn't present is
> because of multiple inheritence could make the call ambiguous. I think that
> such a mechanism should be available and if the call is ambiguous either
> issue an error message or search in order of inclusion. I lean toward the
> error message approach if the function exists in non-pure virtual form in
> more than on base class. In other words, the following should not be
> ambigous.
> class A
> {
> public:
> virtual void Foo(void) = 0;
> };
> class B
> {
> public:
> void Foo(void) {}
> };
> class AB : public A, public B
> {
> public:
> virtual void Foo(void) {this-->Foo(); or super(this)->Foo();}
> };
The problem with both the error message and the search in order of
inclusion is that changes to base classes may break existing derived
classes.
Take the following example:
class A
{
public:
void Foo();
};
class B
{
public:
void Bar();
};
class C : public A, public B
{
public:
void Do() { super->Foo(); super->Bar(); }
};
Perfectly legal, does what we want.
Now we make two different changes:
1) add "public: void Foo();" to class B => class C breaks in case of
error message; still works in case of order of inclusion
2) add "public: void Bar();" to class A => class C breaks in case of
error message; does something unexpected in case of order of inclusion
As soon as you allow the use of the super keyword (or any simular
construct), you get a tight coupling between base and derived classes
since a change in a base class (adding a function) can suddenly break
derived classes.
Regards,
Heinz
---
[ 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 ]
Author: Heinz Huber <Heinz.Huber@elbanet.co.at>
Date: 2000/08/18 Raw View
"Trevor L. Jackson, III" wrote:
>
> Heinz Huber wrote:
>
> > Greg Brewer wrote:
> >
> > [snip proposal and motivation for super]
> >
> > > According tot he columnist though, the big reason it isn't present is
> > > because of multiple inheritence could make the call ambiguous. I think that
> > > such a mechanism should be available and if the call is ambiguous either
> > > issue an error message or search in order of inclusion. I lean toward the
> > > error message approach if the function exists in non-pure virtual form in
> > > more than on base class. In other words, the following should not be
> > > ambigous.
> > > class A
> > > {
> > > public:
> > > virtual void Foo(void) = 0;
> > > };
> > > class B
> > > {
> > > public:
> > > void Foo(void) {}
> > > };
> > > class AB : public A, public B
> > > {
> > > public:
> > > virtual void Foo(void) {this-->Foo(); or super(this)->Foo();}
> > > };
> >
> > The problem with both the error message and the search in order of
> > inclusion is that changes to base classes may break existing derived
> > classes.
> >
> > Take the following example:
> >
> > class A
> > {
> > public:
> > void Foo();
> > };
> >
> > class B
> > {
> > public:
> > void Bar();
> > };
> >
> > class C : public A, public B
> > {
> > public:
> > void Do() { super->Foo(); super->Bar(); }
> > };
> >
> > Perfectly legal, does what we want.
> > Now we make two different changes:
> >
> > 1) add "public: void Foo();" to class B => class C breaks in case of
> > error message; still works in case of order of inclusion
> >
> > 2) add "public: void Bar();" to class A => class C breaks in case of
> > error message; does something unexpected in case of order of inclusion
> >
> > As soon as you allow the use of the super keyword (or any simular
> > construct), you get a tight coupling between base and derived classes
> > since a change in a base class (adding a function) can suddenly break
> > derived classes.
>
> Some these problems derived from and are inevitable consequences of elaborating
> the class hierarchy. One might as well complain that D's explicit use of B::foo()
> causes problem when M (middle) is inserted between B and D in the inheritance
> scheme. The creation of M::foo() causes the *intent* of D's use of B::foo() to be
> ambiguous. Not the original code, the original intent. Should D now be changed
> to use I::foo() or should it remain B::foo()?
>
> The ambiguity stems from the use of B:: to represent both the concept of B:: and
> the concept of D's-first-ancestor-with-a-foo()::. With an explicit distinction
> between B:: and inherit:: (or ancestor::) the author of D can make his intentions
> explicit, which will make handling any subsequent changes simple. Usually that
> the creation of M is aimed at influencing the downstream derived classes, so the
> original code with the explicit upward reference is most likely wrong after the
> change. It is this over explicitness that unnecessarily binds classes into
> interlocking messes.
You only show the case for a single inheritance chain. In this scenario,
there is not problem with a keyword inherited:: or simulars.
> In principle every method of every ancestral class is available within D. But D's
> utilization of ancestral methods is usually sparse, and typically dominated by the
> most derived class having the necessary method. I would interpret a class
> hierarchy in which derived classes explicitly reference ancestral classes other
> than the nearest having the requisite method as a major design flaw.
>
> Providing a shorthand for that typical ancestral usage is a worthwhile refinement
> in the interest of modularity.
Well, I only pointed out that it can lead to big trouble in a multiple
inheritance scenario.
I would have no problem with the concept, if it was restricted to single
inheritance chains. On the other hand, then if a base class decides that
it has to use MI at some point of time, it would possibly break it's
derived classes again. So back to the start.
struct A
{
void Foo();
};
struct B
{
void Foo();
};
struct C : A {};
struct D : C
{
void Do() { inherited::Foo(); }
};
Now change struct C to derive from A and B. Boom. A workaround could be
to provide an explicit Foo() in C. But you still have the problem of my
original example.
Be aware that the changes in this example and the original above do not
happen in the realm of the inherited class. Through some change in the
ancestor classes, class C and struct D suddenly have ambigous calls. Now
imagine that happening deep down in a framework that you have bought.
Bad thing! And tough luck.
One other thing just came to my mind. Therefore a question to the
current rules:
struct A
{
void Foo();
};
struct B { };
struct C : A, B {};
struct D : C
{
void Do() { C::Foo(); }
};
Now introduce the function B::Foo(). What happens under current language
rules?
Regards,
Heinz
---
[ 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 ]
Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 2000/08/18 Raw View
"Chip Jarred" <nospam@nospam.com> wrote in message
news:B5C0DDF9.7B53%nospam@nospam.com...
> in article 8n904f$a6d$1@news.hal-pc.org, Greg Brewer at
> nospam.greg@brewer.net wrote on 8/14/00 11:12 AM:
>
> > That is interesting; I just learned something new. Still I would prefer
the
> > following to not be ambiguous.
> > class A
> > {
> > public:
> > virtual int Foo(void) = 0;
> > };
> > class B
> > {
> > public:
> > int Foo(void) {return 99;}
> > };
> > class AB : public A, public B
> > {
> > public:
> > int Foo(void) {return inherited::Foo();}
> > };
> > // or how about
> > template <class X>
> > class AX : public A, public X
> > {
> > public:
> > int Foo(void) {return inherited::Foo();}
> > };
> > // ....
> > class AX<B> ab;
> > };
> [snip snip snip]
> has not seen. Therefore the call to Foo() MUST be ambiguous.
I disagree that it must be ambiguous. What is needed then is a mechanism
for telling the compiler that the pure virtual function has no definition
and should be ignored by the implicit base class call we are discussing.
This would also help the compiler in issuing an error if an explicit call is
made. While the linker would catch the error, I perfer for the compiler to
catch as many as possible.
> > I think I still prefer not to introduce another keyword.
> > > I don't like adding keywords willy nilly either. There is another
thread
> where someone suggested a keyword "invariant" for "for" loops. I'm
against
> that suggestion. The thing here is that calling inherited member
functions
> is a VERY common idiom in any object oriented language (not just C++).
It's
> so common, and such an integral part of OO design that I strongly believe
> that programmers would benefit from not having to explicitly specify the
> name of the base class when calling an inherited function which is
otherwise
> unambiguous.
But, if a syntactical method for handling it can be established then there
is no need for a new keyword. That is why I suggested "this" + "-" + "-" +
">" in my original post. I do think that "this-->Foo()" might be too easily
confused with "this->Foo()", it seems to me that the second form is rare. I
would expect that it might be too easy for someone to code "this->Foo()"
when they mean "this-->Foo()" and create an infinite loop. But I find it
preferable to adding still another keyword which would break someones
existing code.
Greg
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/18 Raw View
in article 39984911.BB120CD@uswest.net, Steven King at sxking@uswest.net
wrote on 8/15/00 2:19 AM:
> Chip Jarred wrote:
>
>>
>> Actually it is ambiguous. The fact that a member function is pure virtual
>> does not mean that it is not implemented in the class that defines it as
>> pure virtual. Rather it simply means that sub-classes must implement it.
>> You can define a member function, which is pure virtual, and then implement
>> it to give it a default implementation which can be inherited and used in
>> subclasses. The catch is that the subclasses must MUST implement their own
>> version and explicitly call the inherited version.
>>
>> class A
>> {
>> public:
>> virtual void Foo(void) = 0 { /* default implementation here */ }
>> };
>>
>
> You can not do it this way. Section 10.4/2 notes: "a function
> declaration cannot provide both a pure-specifier and a definition" and
> gives as an example:
Yes, I actually knew that, so perhaps I shouldn't have written bad code as
an example. (Head hung sheepishly) My point was that the fact that
declaring a function as pure virtual does not mean that it is not also
defined. At the time an inline declaration seemed like the most concise way
of making the point, but I'm grateful for the ever-vigilant eyes of people
like you to correct me when I do write a thing.
Chip Jarred
---
[ 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 ]
Author: "Trevor L. Jackson, III" <fullmoon@aspi.net>
Date: 2000/08/18 Raw View
Heinz Huber wrote:
> Greg Brewer wrote:
>
> [snip proposal and motivation for super]
>
> > According tot he columnist though, the big reason it isn't present is
> > because of multiple inheritence could make the call ambiguous. I think that
> > such a mechanism should be available and if the call is ambiguous either
> > issue an error message or search in order of inclusion. I lean toward the
> > error message approach if the function exists in non-pure virtual form in
> > more than on base class. In other words, the following should not be
> > ambigous.
> > class A
> > {
> > public:
> > virtual void Foo(void) = 0;
> > };
> > class B
> > {
> > public:
> > void Foo(void) {}
> > };
> > class AB : public A, public B
> > {
> > public:
> > virtual void Foo(void) {this-->Foo(); or super(this)->Foo();}
> > };
>
> The problem with both the error message and the search in order of
> inclusion is that changes to base classes may break existing derived
> classes.
>
> Take the following example:
>
> class A
> {
> public:
> void Foo();
> };
>
> class B
> {
> public:
> void Bar();
> };
>
> class C : public A, public B
> {
> public:
> void Do() { super->Foo(); super->Bar(); }
> };
>
> Perfectly legal, does what we want.
> Now we make two different changes:
>
> 1) add "public: void Foo();" to class B => class C breaks in case of
> error message; still works in case of order of inclusion
>
> 2) add "public: void Bar();" to class A => class C breaks in case of
> error message; does something unexpected in case of order of inclusion
>
> As soon as you allow the use of the super keyword (or any simular
> construct), you get a tight coupling between base and derived classes
> since a change in a base class (adding a function) can suddenly break
> derived classes.
Some these problems derived from and are inevitable consequences of elaborating
the class hierarchy. One might as well complain that D's explicit use of B::foo()
causes problem when M (middle) is inserted between B and D in the inheritance
scheme. The creation of M::foo() causes the *intent* of D's use of B::foo() to be
ambiguous. Not the original code, the original intent. Should D now be changed
to use I::foo() or should it remain B::foo()?
The ambiguity stems from the use of B:: to represent both the concept of B:: and
the concept of D's-first-ancestor-with-a-foo()::. With an explicit distinction
between B:: and inherit:: (or ancestor::) the author of D can make his intentions
explicit, which will make handling any subsequent changes simple. Usually that
the creation of M is aimed at influencing the downstream derived classes, so the
original code with the explicit upward reference is most likely wrong after the
change. It is this over explicitness that unnecessarily binds classes into
interlocking messes.
In principle every method of every ancestral class is available within D. But D's
utilization of ancestral methods is usually sparse, and typically dominated by the
most derived class having the necessary method. I would interpret a class
hierarchy in which derived classes explicitly reference ancestral classes other
than the nearest having the requisite method as a major design flaw.
Providing a shorthand for that typical ancestral usage is a worthwhile refinement
in the interest of modularity.
---
[ 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 ]
Author: llewelly.@@edevnull.dot.com
Date: 2000/08/18 Raw View
"Greg Brewer" <nospam.greg@brewer.net> writes:
> "Chip Jarred" <nospam@nospam.com> wrote in message
> news:B5C0DDF9.7B53%nospam@nospam.com...
> > in article 8n904f$a6d$1@news.hal-pc.org, Greg Brewer at
> > nospam.greg@brewer.net wrote on 8/14/00 11:12 AM:
> >
> > > That is interesting; I just learned something new. Still I would prefer
> the
> > > following to not be ambiguous.
> > > class A
> > > {
> > > public:
> > > virtual int Foo(void) = 0;
> > > };
> > > class B
> > > {
> > > public:
> > > int Foo(void) {return 99;}
> > > };
> > > class AB : public A, public B
> > > {
> > > public:
> > > int Foo(void) {return inherited::Foo();}
> > > };
> > > // or how about
> > > template <class X>
> > > class AX : public A, public X
> > > {
> > > public:
> > > int Foo(void) {return inherited::Foo();}
> > > };
> > > // ....
> > > class AX<B> ab;
> > > };
> > [snip snip snip]
> > has not seen. Therefore the call to Foo() MUST be ambiguous.
>
> I disagree that it must be ambiguous. What is needed then is a mechanism
> for telling the compiler that the pure virtual function has no
> definition
This requires cross module analysis, which is beyond the C++ model of
dumb linkers and compilers that know nothing about them. I have the
impression (which may or may not be correct) that whether C++ should
require cross module analysis was (is?) a hotly debated issue in the
standards committee.
Personally, I would prefer that the next version of C++ require and
take advantage of cross-module analysis - though I believe this
raises some difficult questions w.r.t. to shared libraries and
mixed language environments.
> and should be ignored by the implicit base class call we are discussing.
> This would also help the compiler in issuing an error if an explicit call is
> made. While the linker would catch the error, I perfer for the compiler to
> catch as many as possible.
>
>
> > > I think I still prefer not to introduce another keyword.
> > > > I don't like adding keywords willy nilly either. There is another
> thread
> > where someone suggested a keyword "invariant" for "for" loops. I'm
> against
> > that suggestion. The thing here is that calling inherited member
> functions
> > is a VERY common idiom in any object oriented language (not just C++).
> It's
> > so common, and such an integral part of OO design that I strongly believe
> > that programmers would benefit from not having to explicitly specify the
> > name of the base class when calling an inherited function which is
> otherwise
> > unambiguous.
>
> But, if a syntactical method for handling it can be established then there
> is no need for a new keyword. That is why I suggested "this" + "-" + "-" +
> ">" in my original post. I do think that "this-->Foo()" might be too easily
> confused with "this->Foo()", it seems to me that the second form is rare. I
> would expect that it might be too easy for someone to code
> "this->Foo()"
Until reading this paragraph, I had labored under the delusion that
'this-->Foo()' was a typo, intended to be 'this->Foo()'. Sorry, but
'this-->Foo()' seems more confusing than '=0' or even
'void (Bar::*(Foo::*f)(int))(int)'
Personally, I would rather spend time & effort fixing identifiers to
no longer conflict with new keywords than have more confusing
syntax added to C++.
Exception handling could have been defined to look this:
//No keyword needed here; a try block is just any block followed by
// handlers.
{
//throw an exception like this:
!*%& std::logic_error();
//(The '!*%&' looks like an expletive, and clearly means that
// something is wrong. It is a logical replacement for 'throw'. :-)
}
//This is a handler that catches errors of type 'std::logic_error'
std::logic_error& e:
{
std::cout << e.what() << "\n";
}
Look Ma, no new keywords!
New keywords should of course be chosen carefully, and with thought to
identifier conflicts with existing code (prefer 'throw' over
'raise'), but I do not think new keywords should be avoided at the
expense of clarity.
> when they mean "this-->Foo()" and create an infinite loop. But I find it
> preferable to adding still another keyword which would break someones
> existing code.
[snip]
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/18 Raw View
in article 8njmb8$ife$1@news.hal-pc.org, Greg Brewer at
nospam.greg@brewer.net wrote on 8/18/00 11:06 AM:
[snip]
> I disagree that it must be ambiguous. What is needed then is a mechanism
> for telling the compiler that the pure virtual function has no definition
> and should be ignored by the implicit base class call we are discussing.
> This would also help the compiler in issuing an error if an explicit call is
> made. While the linker would catch the error, I perfer for the compiler to
> catch as many as possible.
Hmmm.... I think adding a keyword "inherited" or something like it would be
beneficial enough to justify it, but I think adding a keyword or some other
syntax to tell the compiler that the that there is no implementation for a
particular pure virtual member function is less compelling. Yes it would
accomplish what you want to do, but then there's nothing to prevent someone
later adding an implementation of the pure virtual function in another
source file, which is only resolved at link time. If this person did not
remember to update the class definition in the header file, the compiler
would have no way of knowing that it should now find an ambiguity, and so it
will resolve it, and then linker will silently link to the wrong inherited
member function. This is situation is subtle enough and difficult enough to
debug, that I would prefer being forced to explicitly resolve it by having
the compiler generate an error.
> But, if a syntactical method for handling it can be established then there
> is no need for a new keyword. That is why I suggested "this" + "-" + "-" +
> ">" in my original post. I do think that "this-->Foo()" might be too easily
> confused with "this->Foo()", it seems to me that the second form is rare. I
> would expect that it might be too easy for someone to code "this->Foo()"
> when they mean "this-->Foo()" and create an infinite loop. But I find it
> preferable to adding still another keyword which would break someones
> existing code.
I applaud your effort to try to figure out a way to make this "inherited"
feature work by extending the syntax, but that should only be done when it
makes some degree of sense. Someone else suggested the user of the word
static as a scope specifier for this (like static::Foo()). I don't like
that because static has an understood meaning that has nothing to do with
accessing inherited members. So exactly what there evils of adding a
keyword? The answer is quite simply that they remove a potential symbol for
the user's name spaces. If you already have a variable or function called
inherited, then adding the inherited keyword will break it. Of course, we
have done that for important enough things... when moving from C to C++
we've added class, even though struct could (and can) be used instead.
We've added public, protected and private. During the course of language
evolution we've added template, try, catch, and throw. I maybe leaving
something out, but you get the idea. The additions of these keywords
probably did break someone's code somewhere, but the benefits to be gained
from having them, even when other syntax could have been used in some
places, outweighed the drawbacks. I think that the act of calling an
inherited function is so common, and so prevalent that it fits into this
category. I also believe that whatever means we use should be consistent
with the current syntax. With the current syntax you call a inherited
function by calling it with the following form
scope::func();
So whatever means is introduced to call inherited functions without
specifying the specific class should still keep the same basic form.
inherited::foo() does just that.
As for the problems of using "this"... this is a pointer. We don't call
inherited functions via pointers. Actually the most common place where we'd
want to use this feature is for an overridden virtual function to call an
inherited implementation of itself. In that case, what happens when you
transform the the pointer (or reference) into a pointer(or reference) of the
base class? The answer is that it still behaves polymorphically. Despite
being called the base class, it is actually the derived class, so the
derived class's implementation is called. OOP depends on this. So what is
"this". "this" is simply a pointer to this object. It is provided so that
the object can refer to itself. In the observer pattern, objects which are
observing (listening) to other objects, need to pass a pointer to themselves
to the objects they are "observing" so that when observed object changes, it
can call back to the observer. That's what this is intended for, and it is
vitally important that this behave polymorphically like other pointers. I
believe that "this" has the wrong meaning to specify a class scope, which is
what calling an inherited function is really all about.
Besides, in your own example you demonstrate that a syntax like
"this-->Foo()" is so similar to "this->Foo()" that it would be a common
mistake resulting in infinite loops... actually it wouldn't. It would
result in infinite recursion, which would cause the program to crash as it
rapidly consumed the entire stack. Yet, you say you prefer it to a keyword.
Why? What you are saying is that you would prefer an obviously more bug
prone syntax over one that would be hard to misuse. And yet the motivation
for such a language feature is to minimize a certain class of bugs by
automating a particular programming practice. So why implement such a
bug-prevention feature in a way that is itself bug prone? If this is just a
knee jerk reaction that new keywords are bad, I think you have to put that
in perspective. If there is enough benefit, a new keyword can be a good
thing. It's like knee jerk reactions to goto statements. Yes, most of the
time the knee jerk reaction is correct, and to date, I haven't seen a good
use of goto in C++, but I have seen good uses of it in C. I think that
those knee jerk responses need to prompt us to look at things more
carefully, not dismiss them out of hand. What is the goal of a good
programming language anyway? Is it to meet some theoretical concept of
language minimalism, or is it to help real programmers write correct,
efficient programs? Often the two go hand in hand, and are thoroughly
compatible goals, but but on the whole, I believe that the latter is far
more important than the former.
Chip Jarred
---
[ 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 ]
Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 2000/08/19 Raw View
<llewelly.@@edevnull.dot.com> wrote in message
news:m2vgwygxof.fsf@brownie.frogger.foobar.snot...
> "Greg Brewer" <nospam.greg@brewer.net> writes:
> > I disagree that it must be ambiguous. What is needed then is a
mechanism
> > for telling the compiler that the pure virtual function has no
> > definition
>
> This requires cross module analysis, which is beyond the C++ model of
> dumb linkers and compilers that know nothing about them. I have the
> impression (which may or may not be correct) that whether C++ should
> require cross module analysis was (is?) a hotly debated issue in the
> standards committee.
>
> Personally, I would prefer that the next version of C++ require and
> take advantage of cross-module analysis - though I believe this
> raises some difficult questions w.r.t. to shared libraries and
> mixed language environments.
That may or may not be the case. My thinking is that syntax along the lines
of
int Foo(void) = 0 {};
Might be used to explicitly tell the compile that this function has no
definition. Given that information, the compiler is free to ignore it when
doing a implicit base class search.
> Until reading this paragraph, I had labored under the delusion that
> 'this-->Foo()' was a typo, intended to be 'this->Foo()'. Sorry, but
> 'this-->Foo()' seems more confusing than '=0' or even
> 'void (Bar::*(Foo::*f)(int))(int)'
Now, I am confused. "this-->Foo()' is an excution declaration telling the
compiler to call an inherited instance of Foo. The two examples you give
are declaration syntax.
Greg
---
[ 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 ]
Author: "Trevor L. Jackson, III" <fullmoon@aspi.net>
Date: 2000/08/19 Raw View
Heinz Huber wrote:
> "Trevor L. Jackson, III" wrote:
> >
> > Heinz Huber wrote:
> >
> > > As soon as you allow the use of the super keyword (or any simular
> > > construct), you get a tight coupling between base and derived classes
> > > since a change in a base class (adding a function) can suddenly break
> > > derived classes.
> >
> > Some these problems derived from and are inevitable consequences of elaborating
> > the class hierarchy. One might as well complain that D's explicit use of B::foo()
> > causes problem when M (middle) is inserted between B and D in the inheritance
> > scheme. The creation of M::foo() causes the *intent* of D's use of B::foo() to be
> > ambiguous. Not the original code, the original intent. Should D now be changed
> > to use I::foo() or should it remain B::foo()?
> >
> > The ambiguity stems from the use of B:: to represent both the concept of B:: and
> > the concept of D's-first-ancestor-with-a-foo()::. With an explicit distinction
> > between B:: and inherit:: (or ancestor::) the author of D can make his intentions
> > explicit, which will make handling any subsequent changes simple. Usually that
> > the creation of M is aimed at influencing the downstream derived classes, so the
> > original code with the explicit upward reference is most likely wrong after the
> > change. It is this over explicitness that unnecessarily binds classes into
> > interlocking messes.
>
> You only show the case for a single inheritance chain. In this scenario,
> there is not problem with a keyword inherited:: or simulars.
>
> > In principle every method of every ancestral class is available within D. But D's
> > utilization of ancestral methods is usually sparse, and typically dominated by the
> > most derived class having the necessary method. I would interpret a class
> > hierarchy in which derived classes explicitly reference ancestral classes other
> > than the nearest having the requisite method as a major design flaw.
> >
> > Providing a shorthand for that typical ancestral usage is a worthwhile refinement
> > in the interest of modularity.
>
> Well, I only pointed out that it can lead to big trouble in a multiple
> inheritance scenario.
Changing from single to multiple inheritance can cause an arbitrarily large amount of
trouble.
>
> I would have no problem with the concept, if it was restricted to single
> inheritance chains. On the other hand, then if a base class decides that
> it has to use MI at some point of time, it would possibly break it's
> derived classes again. So back to the start.
But the concept is still useful in the MI situation. Perhaps even more so. Consider a
method of struct D : B1, B2 {} that needs to call an inherited function that appears in
a single base class. It can still use the simple ancestor::foo() approach. But if both
parent classes have a foo() somewhere in their ancestry one could use
ancestor::B1::foo(). In this case the B1:: specifier does not mean B1, but the
ancestral tree that contains B1. Thus if D's inheritance were changed to struct D : I1,
B2 {} the expression would resolve to the nearest ancestor in the line of descent from
B1 that has a foo().
Thus at any given ambiguous point in the ancestral tree one could supply just enough
information to resolve the ambiguity without overspecifying details that will later
cause trouble.
---
[ 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 ]
Author: llewelly.@@edevnull.dot.com
Date: 2000/08/21 Raw View
"Greg Brewer" <nospam.greg@brewer.net> writes:
> <llewelly.@@edevnull.dot.com> wrote in message
> news:m2vgwygxof.fsf@brownie.frogger.foobar.snot...
> > "Greg Brewer" <nospam.greg@brewer.net> writes:
> > > I disagree that it must be ambiguous. What is needed then is a
> mechanism
> > > for telling the compiler that the pure virtual function has no
> > > definition
> >
> > This requires cross module analysis, which is beyond the C++ model of
> > dumb linkers and compilers that know nothing about them. I have the
> > impression (which may or may not be correct) that whether C++ should
> > require cross module analysis was (is?) a hotly debated issue in the
> > standards committee.
> >
> > Personally, I would prefer that the next version of C++ require and
> > take advantage of cross-module analysis - though I believe this
> > raises some difficult questions w.r.t. to shared libraries and
> > mixed language environments.
>
> That may or may not be the case. My thinking is that syntax along the lines
> of
> int Foo(void) = 0 {};
I misread the words 'a mechanism for telling the compiler that the
pure virtual function has no definition' in your earlier post; I
thought you wanted the compiler to automagically figure out that the
pure virtual function had no definition.
>
> Might be used to explicitly tell the compile that this function has no
> definition. Given that information, the compiler is free to ignore it when
> doing a implicit base class search.
>
> > Until reading this paragraph, I had labored under the delusion that
> > 'this-->Foo()' was a typo, intended to be 'this->Foo()'. Sorry, but
> > 'this-->Foo()' seems more confusing than '=0' or even
> > 'void (Bar::*(Foo::*f)(int))(int)'
>
> Now, I am confused. "this-->Foo()' is an excution declaration telling the
> compiler to call an inherited instance of Foo. The two examples you give
> are declaration syntax.
I was not trying to suggest alternatives; I was presenting existing
examples of (what I consider to be) confusing syntax in C++, and
comparing your 'this-->Foo()' to them.
If had tried to suggest an alternative, I would have added my support
to the 'inherited::' syntax.
I do not much care whether or not this extension ever ends up in C++,
but if it does, I want it to have readable syntax and well-defined
semantics. In particular, I usually prefer carefully chosen keywords
to operators and operator-like syntax. (Operators for mathematical
operations being one exception.)
(On a different note, it just occured to me that 'inherited::' has an
obvious meaning in using declarations, and 'this-->Foo()' does not.)
---
[ 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 ]
Author: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@attglobal.net>
Date: 2000/08/11 Raw View
>>>>>>>>>>>>>>>>>> Urspr=FCngliche Nachricht <<<<<<<<<<<<<<<<<<
Am 09.08.00, 20:25:31, schrieb pedwards@dmapub.dma.org (Phil Edwards) zum
Thema Re: super in C++:
> Greg Brewer <nospam.greg@brewer.net> wrote:
> + The question was how to
> + code the function so that execution is passed up one level. The answ=
er
was
> + that you can't easily -- he offered a kludge solution -- implement
something
> + like the Java super keyword because of multiple inheritence.
> Or all of the other languages with a 'super' or super-ish keyword.
> I believe Java followed the Smalltalk model in this case.
> According to the D&E (which everyone should definitely read (no I don't
> get paid for saying that)), such a thing was considered but then droppe=
d
> because you don't need it. You can implement it with the language
itself:
> class Foo { .....};
> class Bar : public Foo
> {
> typedef Foo super;
> ....
> blah blah blah { super::some_foo_thing(); }
> }
Well, I once worked with an early 'C with C++-ish extensions' which did
have a keyword 'inherited'. I liked it pretty much, and when I finally
got my hands on real C++, most of my derived classes started with code
like the above (but the private type was named 'inherited'.
> If the ancestry of Bar changes, only one line of code (the typedef) nee=
d
> be altered.
And you can keep it very close to the line where the inheritance
relationship is declared, so chances are unusually high that you wont
forget to change it.
> With multiple inheritance... I don't know. I suspect the solution woul=
d
be
> quite situation-dependant, but I have nothing with which to back that u=
p.
> Maybe 'super1' through 'superN'.
That is actually the point where D&E misses the point (IMHO). You can't
implement it with the language itself in the presence of multiple
inheritance. IIRC D&E argues that in this case we may introduce unwanted
ambiguities.
But IMHO that's fine: If more than one base class provides a function
signature, a call to that function should be ambiguous, unless you
explicitly select which one to choose.
In that case, using 'inherited' cannot and should not resolve it.
The strange thing about 'inherited' is, that it would not easily fit into
the taxonomy of C++ entities.
- It is not a type or class name (in the case of MI =96 otherwise the
typedef approach would do).
- It names a scope, but it is not a namespace name (the scope is a union
of class scopes).
But it would have to be defined as something something like:
- It can be used only in a nested-name-specifier
- It will be found by name lookup in any class scope
- If the class it is found in has no base classes, the program is
ill-formed
- Otherwise it names a scope that is the union of the class scopes of all
immediate base classes of the class in which it was looked up; lookup of
the rest of the qualified-id it is part of will proceed in that composite
scope.
IOW: If we have a class A and 'inherited::' occurs within that scope (or
prefixed by 'A::') namelookup proceeds as if in A, but ignoring any
members declared (directly) in A.
So I think this could be something useful.
Right now I don't have an example of where it would be really useful,
that also is good design, but...
template <class T> struct A { void foo(T); };
struct B { static void foo(B const& ); };
class X : A<int>, A<short>, A<std::complex<double> >, public B
{
void pre_foo();
public:
template<class T> void foo(T t =3D T())
{
pre_foo();
inherited::foo(t);
}
};
template <class T> void bar(X& x) { x.template foo<T>(); }
int main() { X x; bar<short>(x); bar<B>(x);
x.foo(std::complex<double>(1.2,3.4)); }
how can I write X::foo<T>(T) without 'inherited' ? (no way to use
overloading =96 look at bar).
Please, dont show me 'workarounds' using a helper class template
(nested), partial or explicit specialization, friendship grants and
explicitly passing around parameters instead of relying on 'this'. IMHO
that is more than a workaround =96 and it really obscures what is going o=
n.
[Some observers may notice that this is also an argument in favor of
function template partial specialization].
Regards, J=F6rg
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/12 Raw View
in article 8mudqj$b9s$1@news.hal-pc.org, Greg Brewer at
nospam.greg@brewer.net wrote on 8/10/00 9:30 AM:
> Although I'm not sure what the D&E is, I am familar with using typedef to
> provide access to base functions. It sounds reasonable but I have found it
> less that useful in practice. One problem is that I make extensive use of
> multiple inheritence and so I can't use the single typedef. And once you
> move to multiple typedefs, you have to remember which is for which base
> class. It's too easy to just code the base class name in rather than look
> up the number for this base class. And looking up information like this is
> what I want my compiler to do. Looking up stuff damages the work flow.
>
> Greg
True. That's something else I should have said in my earlier response to the
typedef suggestion. An inherited keyword could do many things that you
can't do with a typedef: Give errors on ambiguity, follow the inheritence
change, even when you add intermediate base classes, and work with multiple
base classes (so long as there is no ambiguity).
Chip Jarred
---
[ 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 ]
Author: pedwards@dmapub.dma.org (Phil Edwards)
Date: 2000/08/13 Raw View
=?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@attglobal.net> wrote:
+ That is actually the point where D&E misses the point (IMHO). You can't
+ implement it with the language itself in the presence of multiple
+ inheritance. IIRC D&E argues that in this case we may introduce unwanted
+ ambiguities.
+ But IMHO that's fine: If more than one base class provides a function
+ signature, a call to that function should be ambiguous, unless you
+ explicitly select which one to choose.
+ In that case, using 'inherited' cannot and should not resolve it.
I agree with you, I think. My suggestion of superN would have only saved
some typing... maybe. If you have to resolve inheritance ambiguities,
then typedefs wouldn't have helped in any case.
My gut feeling is that we shouldn't be looking for ways to make MI really
easy.
+ But it would have to be defined as something something like:
+ - It can be used only in a nested-name-specifier
+ - It will be found by name lookup in any class scope
+ - If the class it is found in has no base classes, the program is
+ ill-formed
+ - Otherwise it names a scope that is the union of the class scopes of all
+ immediate base classes of the class in which it was looked up; lookup of
+ the rest of the qualified-id it is part of will proceed in that composite
+ scope.
+
+ IOW: If we have a class A and 'inherited::' occurs within that scope (or
+ prefixed by 'A::') namelookup proceeds as if in A, but ignoring any
+ members declared (directly) in A.
+
+ So I think this could be something useful.
Ah, now that would be interesting. Do you know of any experiments where
somebody has tried this for recent C++?
Phil
---
[ 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 ]
Author: Chip Jarred <nospam@nospam.com>
Date: 2000/08/13 Raw View
in article 8ms6kv$1m4$1@dmapub.dma.org, Phil Edwards at
pedwards@dmapub.dma.org wrote on 8/9/00 1:25 PM:
>
> Greg Brewer <nospam.greg@brewer.net> wrote:
> + The question was how to
> + code the function so that execution is passed up one level. The answer was
> + that you can't easily -- he offered a kludge solution -- implement something
> + like the Java super keyword because of multiple inheritence.
>
> Or all of the other languages with a 'super' or super-ish keyword.
> I believe Java followed the Smalltalk model in this case.
>
> According to the D&E (which everyone should definitely read (no I don't
> get paid for saying that)), such a thing was considered but then dropped
> because you don't need it. You can implement it with the language itself:
>
> class Foo { .....};
>
> class Bar : public Foo
> {
> typedef Foo super;
> ....
>
> blah blah blah { super::some_foo_thing(); }
> }
>
> If the ancestry of Bar changes, only one line of code (the typedef) need
> be altered.
>
> With multiple inheritance... I don't know. I suspect the solution would be
> quite situation-dependant, but I have nothing with which to back that up.
> Maybe 'super1' through 'superN'.
Yes, it can be done within the current language, but that solution suffers
from a somewhat more limited form of the same problem as the original
situation it's intended to remedy. Specifically, it's easy to forget to
define it for each and every subclass. For instance...
class A
{
public:
virtual void Foo();
};
class B : public A
{
public:
typedef A inherited; // I prefer this as a keyword instead of super
virtual void Foo() { inherited::Foo(); }
};
class C : public B
{
public:
typedef B inherited;
virtual void Foo() { inherited::Foo(); }
}
This works fine, but now let's introduce what would be a very simple and
very common error... forgetting to put the typedef in class B. In that case
C::Foo() would call A::Foo() instead of B::Foo() which is what was intended.
They keyword makes this process more robust, and allows it to be checked by
the compiler. Fortunately, the more common mistake (of changing B's base
class, but forgetting to make the corresponding change in the typedef) would
be caught by the compiler.
Chip Jarred
---
[ 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 ]
Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 2000/08/09 Raw View
Has anyone read Q&A in C++ User's Journal? www.cuj.com/current/qa.html
The question deals with access to the base class. Given
class A
{
public:
virtual void Foo(void) {}
};
and
class AA : public A
{
public:
virtual void Foo(void) {A::Foo();}
};
the questioner had a situation where an intermediate class was added so that
it was now
class A
{
public:
virtual void Foo(void) {}
};
class A1 : public A
{
public:
virtual void Foo(void) {A::Foo();}
};
class AA : public A1
{
public:
virtual void Foo(void) {A::Foo();}
};
with his problem being that he failed to corrected the virtual function so
that it passed execution to the intermediate class. The question was how to
code the function so that execution is passed up one level. The answer was
that you can't easily -- he offered a kludge solution -- implement something
like the Java super keyword because of multiple inheritence.
Personally, I hate the idea of adding another keyword to C++ but I think
adding a clean mechanism for moving up to the next base class would be good.
I'm thinking of something like "this-->Foo(); (note double -)" might be
workable. It might be too difficult because it is the post decrement
operator followed by the greater than operator but it's one idea.
According tot he columnist though, the big reason it isn't present is
because of multiple inheritence could make the call ambiguous. I think that
such a mechanism should be available and if the call is ambiguous either
issue an error message or search in order of inclusion. I lean toward the
error message approach if the function exists in non-pure virtual form in
more than on base class. In other words, the following should not be
ambigous.
class A
{
public:
virtual void Foo(void) = 0;
};
class B
{
public:
void Foo(void) {}
};
class AB : public A, public B
{
public:
virtual void Foo(void) {this-->Foo(); or super(this)->Foo();}
};
---
[ 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 ]
Author: pedwards@dmapub.dma.org (Phil Edwards)
Date: 2000/08/09 Raw View
Greg Brewer <nospam.greg@brewer.net> wrote:
+ The question was how to
+ code the function so that execution is passed up one level. The answer was
+ that you can't easily -- he offered a kludge solution -- implement something
+ like the Java super keyword because of multiple inheritence.
Or all of the other languages with a 'super' or super-ish keyword.
I believe Java followed the Smalltalk model in this case.
According to the D&E (which everyone should definitely read (no I don't
get paid for saying that)), such a thing was considered but then dropped
because you don't need it. You can implement it with the language itself:
class Foo { .....};
class Bar : public Foo
{
typedef Foo super;
....
blah blah blah { super::some_foo_thing(); }
}
If the ancestry of Bar changes, only one line of code (the typedef) need
be altered.
With multiple inheritance... I don't know. I suspect the solution would be
quite situation-dependant, but I have nothing with which to back that up.
Maybe 'super1' through 'superN'.
Luck++;
Phil
---
[ 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 ]
Author: llewelly.@@edevnull.dot.com
Date: 2000/08/09 Raw View
pedwards@dmapub.dma.org (Phil Edwards) writes:
> Greg Brewer <nospam.greg@brewer.net> wrote:
> + The question was how to
> + code the function so that execution is passed up one level. The answer was
> + that you can't easily -- he offered a kludge solution -- implement something
> + like the Java super keyword because of multiple inheritence.
>
> Or all of the other languages with a 'super' or super-ish keyword.
> I believe Java followed the Smalltalk model in this case.
>
> According to the D&E (which everyone should definitely read (no I don't
> get paid for saying that)), such a thing was considered but then dropped
> because you don't need it. You can implement it with the language itself:
>
> class Foo { .....};
>
> class Bar : public Foo
> {
> typedef Foo super;
> ....
>
> blah blah blah { super::some_foo_thing(); }
> }
>
> If the ancestry of Bar changes, only one line of code (the typedef) need
> be altered.
>
> With multiple inheritance... I don't know. I suspect the solution would be
> quite situation-dependant, but I have nothing with which to back that up.
> Maybe 'super1' through 'superN'.
Seems like it would be easy to forget which base class was super3.
Personally, I prefer to use the name of the base class in question. :-)
[snip]
---
[ 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 ]
Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 2000/08/10 Raw View
Although I'm not sure what the D&E is, I am familar with using typedef to
provide access to base functions. It sounds reasonable but I have found it
less that useful in practice. One problem is that I make extensive use of
multiple inheritence and so I can't use the single typedef. And once you
move to multiple typedefs, you have to remember which is for which base
class. It's too easy to just code the base class name in rather than look
up the number for this base class. And looking up information like this is
what I want my compiler to do. Looking up stuff damages the work flow.
Greg
"Phil Edwards" <pedwards@dmapub.dma.org> wrote in message
news:8ms6kv$1m4$1@dmapub.dma.org...
>
> Greg Brewer <nospam.greg@brewer.net> wrote:
> + The question was how to
> + code the function so that execution is passed up one level. The answer
was
> + that you can't easily -- he offered a kludge solution -- implement
something
> + like the Java super keyword because of multiple inheritence.
>
> Or all of the other languages with a 'super' or super-ish keyword.
> I believe Java followed the Smalltalk model in this case.
>
> According to the D&E (which everyone should definitely read (no I don't
> get paid for saying that)), such a thing was considered but then dropped
> because you don't need it. You can implement it with the language itself:
> class Foo { .....};
> class Bar : public Foo
> {
> typedef Foo super;
> ....
> blah blah blah { super::some_foo_thing(); }
> }
> If the ancestry of Bar changes, only one line of code (the typedef) need
> be altered.
> With multiple inheritance... I don't know. I suspect the solution would
be
> quite situation-dependant, but I have nothing with which to back that up.
> Maybe 'super1' through 'superN'.
---
[ 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 ]