Topic: Proposed enhancement - 'thisclass' keyword for virtual function parameters
Author: ark@research.att.com (Andrew Koenig)
Date: 1998/05/22 Raw View
In article <6k0tpt$7m6@netlab.cs.rpi.edu>,
Oleg Zabluda <zabluda@math.psu.edu> wrote:
> BTW, is there any reason why contravariant arguments weren't
> allowed in C++ at the same time covariant return types were
> allowed? Was there ever any discussion of that?
The main reason is that they're not terribly useful.
--
--Andrew Koenig
ark@research.att.com
http://www.research.att.com/info/ark
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/05/22 Raw View
Oleg Zabluda wrote:
>
> Fergus Henderson <fjh@cs.mu.OZ.AU> wrote:
> : Oleg Zabluda <zabluda@math.psu.edu> writes:
>
> : >Markus Kohler <markus_kohler@bbn.hp.com> wrote:
> : >: Generally your problem is related to covariant typing.
> : >: C++ allows only covariant return values. That means you can
return a
> : >: derived type in a subclass.
> : >: C++ does not allow covariant arguments :-(
> : >: Eiffel for example supports that
> : >
> : >I don't know Eiffel, but intend to learn it. Also, being a
> : >mathematician, I am not very familiar with cs lingvo. But,
> : >shouldn't it be _contravariant_ arguments?
>
> : That depends. If you're asking about Eiffel, then no,
> : Eiffel does support covariant arguments. If you're asking
> : about programming language design, then yes, arguments should be
> : contravariant -- IMHO, allowing covariant arguments is a really
> : bad idea. I think the Eiffel design was a poor decision.
> : It would be foolish for C++ to allow covariant arguments.
>
> Right. Jeez. That's a major error in Eiffel. Foolish
> is the mildest way you can put it.
>
> BTW, is there any reason why contravariant arguments weren't
> allowed in C++ at the same time covariant return types were
> allowed? Was there ever any discussion of that?
I guess because it would have bad interaction with overloading.
Especially it could silently change the meaning ov a conforming program:
class A {};
class B: public A {};
class X
{
public:
virtual void f(B) {}
};
class Y: public X
{
public:
void f(A) { cout << "Y" << endl; }
};
class Z: public Y
{
public:
void f(A) { cout << "Z" << endl; }
};
int main()
{
Y* py=new Z;
py->f(A());
}
Currently what happens is the following:
Y defines a new, nonvirtual member function void f(A), which overloads
(and hides) the virtual function void X::f(B). Z also defines a new
member function void f(A) - since Y::f(A) is not virtual, the new
member function Z::f(A) does not override Y::f(A). Now, in the main
program, f(A) is called with a pointer to Y, so since f(A) is not
virtual, Y::f(A) is called. The program therefore prints "Y".
With contravariant arguments, the following would happen:
Y defines void Y::f(A). Since B is derived from A, it overrides
X::f(B) due to contravariant argument types. Especially, since it
overrides a virtual function, it is virtual itself. Now Z overrides
the virtual function void Y::f(A). The pointer call in main now
resolves to Z::f(A) (since it's a virtual function), and therefore
the program prints "Z".
There are also other problems which again are caused by overloading:
class A {};
class B: public A {};
class C: public A {};
class X
{
public:
virtual void f(B);
virtual void f(C);
};
class Y: public X
{
public:
void f(A); // which function does that override?
};
Note that those problems cannot occur with return types, since
you cannot overload a function on its return type.
However, I could think of a mechanism where you'd specify
the function which you override explicitly:
class A {};
class B: public A {};
class X
{
public:
virtual void f(B);
virtual void g(B);
};
class Y: public X
{
public:
void f(A); // current meaning: new, unrelated function
void g(A) overrides void X::g(B);
// explicitly gives the function it overrides.
};
There could be a shorthand "overrides;" which means "overrides
the function with the same signature (maybe except for covariant
return type).
It could also allow compiler checks not possible today,
like in the following example:
class Base
{
public:
virtual void do_something();
};
class Derived: public Base
{
public:
void dosomething() overrides;
// error: no overridable void dosomething() in base class
};
It also could be extended to give a natural renaming feature:
class Base1
{
public:
virtual void f();
};
class Base2
{
public:
virtual void f();
};
class Derived: public Base1, public Base2
{
public:
void f1() overrides void Base1::f();
void f2() overrides void Base2::f();
};
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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: fjh@cs.mu.OZ.AU (Fergus Henderson)
Date: 1998/05/20 Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:
>Markus Kohler <markus_kohler@bbn.hp.com> wrote:
>: Generally your problem is related to covariant typing.
>: C++ allows only covariant return values. That means you can return a
>: derived type in a subclass.
>: C++ does not allow covariant arguments :-(
>: Eiffel for example supports that
>
>I don't know Eiffel, but intend to learn it. Also, being a
>mathematician, I am not very familiar with cs lingvo. But,
>shouldn't it be _contravariant_ arguments?
That depends. If you're asking about Eiffel, then no,
Eiffel does support covariant arguments. If you're asking
about programming language design, then yes, arguments should be
contravariant -- IMHO, allowing covariant arguments is a really
bad idea. I think the Eiffel design was a poor decision.
It would be foolish for C++ to allow covariant arguments.
--
Fergus Henderson <fjh@cs.mu.oz.au> | "I have always known that the
pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3 | -- the last words of T. S.
Garp.
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
---
[ 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: Pete Becker <petebecker@acm.org>
Date: 1998/05/20 Raw View
Oleg Zabluda wrote:
>
> I don't know Eiffel, but intend to learn it. Also, being a
> mathematician, I am not very familiar with cs lingvo. But,
> shouldn't it be _contravariant_ arguments?
Only if you think type safety is important. <g>
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/05/21 Raw View
Fergus Henderson <fjh@cs.mu.OZ.AU> wrote:
: Oleg Zabluda <zabluda@math.psu.edu> writes:
: >Markus Kohler <markus_kohler@bbn.hp.com> wrote:
: >: Generally your problem is related to covariant typing.
: >: C++ allows only covariant return values. That means you can return a
: >: derived type in a subclass.
: >: C++ does not allow covariant arguments :-(
: >: Eiffel for example supports that
: >
: >I don't know Eiffel, but intend to learn it. Also, being a
: >mathematician, I am not very familiar with cs lingvo. But,
: >shouldn't it be _contravariant_ arguments?
: That depends. If you're asking about Eiffel, then no,
: Eiffel does support covariant arguments. If you're asking
: about programming language design, then yes, arguments should be
: contravariant -- IMHO, allowing covariant arguments is a really
: bad idea. I think the Eiffel design was a poor decision.
: It would be foolish for C++ to allow covariant arguments.
Right. Jeez. That's a major error in Eiffel. Foolish
is the mildest way you can put it.
BTW, is there any reason why contravariant arguments weren't
allowed in C++ at the same time covariant return types were
allowed? Was there ever any discussion of that?
Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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: Marc Girod <girod@stybba.ntc.nokia.com>
Date: 1998/05/27 Raw View
>>>>> "ark" == Andrew Koenig <ark@research.att.com> writes:
ark> In article <6k0tpt$7m6@netlab.cs.rpi.edu>,
ark> Oleg Zabluda <zabluda@math.psu.edu> wrote:
>> BTW, is there any reason why contravariant arguments weren't
>> allowed in C++ at the same time covariant return types were
>> allowed? Was there ever any discussion of that?
ark> The main reason is that they're not terribly useful.
A bit short, don't you think so?
I have found myself trying to circumvent this is the past.
My problem was with template classes building up "dual hierarchies".
Say you have the classic:
class OBase;
class VBase {
virtual void go(OBase&) = 0;
virtual ~VBase() {}
};
template <class T> Vehicle: virtual private VBase {
typedef typename T::Operator Op;
// I'd have liked to dedicate this interface to Op through
// friendship, but this is a different story...
public:
virtual void go(Op&) = 0;
};
// I leave the remaining half and the implementations
In this kind of example, one (at least I :-) can argue that the lack
of support for contravariant arguments builds up a "lack of
orthogonality" problem, preventing me from moving from a non-template
definition of Vehicle to a template one (in order to tighten type
awareness in my program).
I admit right now that I would not be out of the water just with
contravariant arguments... This would then furiously look soon like a
multiple dispatch problem (see the posting by Christopher Eltschka).
Anyway, I'd argue that contravariant arguments could be "terribly
useful" (and that the kind of construction above is --mind you--
extremely widely applicable).
Best Regards!
--
Marc Girod Valimo 1/2 Voice: +358-9-511 63331
Nokia Telecommunications P.O. Box 315 Mobile: +358-40-569 7954
NWS/NMS/NMS for Data 00045 NOKIA Group Fax: +358-9-511 63310
Finland marc.girod@ntc.nokia.com
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
---
[ 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: Marc Girod <girod@stybba.ntc.nokia.com>
Date: 1998/05/28 Raw View
Ahum...
A somewhat embarrassed correction to my yesterday followup to Andrew
Koenig (thanks to Robert Martin).
>>>>> "MG" == Marc Girod <girod@stybba.ntc.nokia.com> writes:
MG> Anyway, I'd argue that contravariant arguments could be "terribly
MG> useful"
This was about covariant arguments, and thus, out of scope.
Sorry.
--
Marc Girod Valimo 1/2 Voice: +358-9-511 63331
Nokia Telecommunications P.O. Box 315 Mobile: +358-40-569 7954
NWS/NMS/NMS for Data 00045 NOKIA Group Fax: +358-9-511 63310
Finland marc.girod@ntc.nokia.com
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/05/19 Raw View
Markus Kohler <markus_kohler@bbn.hp.com> wrote:
: Generally your problem is related to covariant typing.
: C++ allows only covariant return values. That means you can return a
: derived type in a subclass.
: C++ does not allow covariant arguments :-(
: Eiffel for example supports that
I don't know Eiffel, but intend to learn it. Also, being a
mathematician, I am not very familiar with cs lingvo. But,
shouldn't it be _contravariant_ arguments? That is, given
"pseudo C++":
class Base {
public:
virtual A* f(X*);
};
class Derived : public Base {
public:
virtual B* f(Y*);
};
Derived::f() overrides Base::f() iff B* is convertible to A*
(covariancy), and X* is convertible to Y* (contravariancy).
Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
---
[ 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: Markus Kohler <markus_kohler@bbn.hp.com>
Date: 1998/05/12 Raw View
Dylan Nicholson wrote:
>
> Hi
>
> I had an idea recently that partially solves a particularly annoying problem
> with C++, that is, the inability to successfully virtualize functions that
> take as parameters references to other objects of the same class - eg I
> can't do
>
> class Base
> {
> int i;
> public:
> Base(int n) { i = n; }
> virtual Base& operator = (const Base& rhs)
> {
> i = rhs.i;
> return *this;
> }
> };
>
> then in a Derived class, override the operator = function so that
> the class of 'rhs' is the Derived class, not the Base class.
>
> My solution is to introduce a 'thisclass' keyword, thisclass standing
> for the type of the object being defined, so the above would read:
>
> class Base
> {
> int i;
> public:
> Base(int n) { i = n; }
> virtual thisclass& operator = (const thisclass& rhs)
> {
> i = rhs.i;
> return *this;
> };
> };
>
> If you wished to override operator = in a Derived class, it would be
> trivial:
>
> class Derived : public Base
> {
> int j;
> public:
> Derived() { j = 0 };
> virtual thisclass& operator = (const thisclass& rhs)
> {
> ((Base&)*this) = rhs;
> j = rhs.j;
> return *this;
> }
> };
>
> The compiler would only allow calls to a virtual member defined like this
> if it knew at compile time that all parameters were of the most derived class
> of the class of invoking object. If one or more of them were of a lesser
> type, it would probably be forced to statically bind to the member defined
> for the class of the invoking object (although this could be semantically
> problematic, maybe a warning would be in better order).
> This should give an idea of what I have in mind:
>
> void DoSomething1(Base& obj1, Derived& obj2)
> {
> ...
> obj1 = obj2; // virtual operator = is called, because obj2 is a
> Derived
> // thus if obj1 is really a Derived, Derived::operator
> =
> // will be called.
> ...
> }
>
> void DoSomething2(Base& obj1, Base& obj2)
> {
> ...
> obj1 = obj2; // Base::operator = is forcibly called, because obj2 is not
> // known to be Derived at compile time. A warning might
> // be displayed.
> }
>
> int main()
> {
> Derived d1(1);
> Derived d2(2);
> ...
> DoSomething1(d1, d2);
> DoSomething2(d1, d2);
> ...
> return 0;
> }
>
> If we stuck to compile time binding like this, its usage would be quite
> restrictive, because adding another class derived from either Base or Derived
> would mean that the virtual function could not be safely called in DoSomething1.
> Alternatively, I could be truly radical and suggest that at run-time it
> is negotiated which operator to call based on the type of the parameters
> passed in. The simplest thing would be I guess to compare the values of the
> virtual function pointers (assuming we're using vtbls - how many compilers
> don't???) on the lhs and rhs objects and if they are same, it is safe to call
> the virtual function for the lhs object. A more complex (but perhaps more
> useful) approach would be to determine the 'lowest common demoninator' class
> (ie the one from which all objects are derived), and call its member, however
> this would require a fair bit of run-time type info (although I suspect not a
> lot more than what is used in exception handling code).
>
> Anyway, I'm interested in what people think...
>
You might take a look at Luca Cardelli's home page :
http://www.luca.demon.co.uk/.
You can find there a bunch of excellent documents about this and other
problems
with type systems. Especially "On Binary Methods" should be interesting
for you.
I also have another article from the web which exactly describes what
you propose.
Unfortunately I lost the reference :-(
The author is Kim B. Bruce Williams College and the title is "Typing in
object-oriented
languages: Achieving expressiveness and safety".
Generally your problem is related to covariant typing.
C++ allows only covariant return values. That means you can return a
derived type in a subclass.
C++ does not allow covariant arguments :-(
Eiffel for example supports that
Markus
--
[This signature is intentionally left blank.]
[ 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: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/05/04 Raw View
Dylan Nicholson wrote:
>
> Hi
>
> I had an idea recently that partially solves a particularly annoying problem
> with C++, that is, the inability to successfully virtualize functions that
> take as parameters references to other objects of the same class - eg I
[excess quoting deleted --hlh]
You can already do what you want today:
class Base
{
int i;
public:
Base(int n): i(n) {}
virtual Base& operator=(const Base& rhs)
{
i = rhs.i;
return *this;
}
};
class Derived: public Base
{
int j;
public:
Derived(): Base(3), j(0) {}
virtual Derived& operator=(const Derived& rhs)
{
Base::operator==((Base&)rhs);
j=rhs.j;
}
virtual Derived& operator=(const Base& rhs)
{
Derived* rhs_d=dynamic_cast<Derived*>(&rhs);
if(rhs_d !=NULL)
return (*this)=(*rhs_d); // calls Derived& operator=(const
Derived&)
else
{
Base::operator==(rhs);
return *this;
}
}
};
I admit it's more typing, but it does the thing you want:
If the left side is a derived, and the right side is as well,
Derived& Derived::operator=(const Derived&) is called. If the left side
is a base, Base& Base::operator=(const Base&) is called. If the left
side is a Derived, but the right side is a Base, the Base part is
assigned (via Base& Base::operator=(const Base&), and the Derived only
part is untouched (alternatively you could set it to other values,
like default values, in the else block of Derived::operator=(const
Base&)
if this makes more sense in your case). It even works for different
derivations from Base, since Derived would treat a Derived2 which
does derive from Base, not Derived, exactly the same as if it were
a Base (i.e. copy the Base part, and treat its own Derived only part
in a consistant manner, written in Derived).
[ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
[ 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 ]