Topic: inlining virtual functions
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/07/02 Raw View
In article <4r0bg3$fd5@silver.jba.co.uk> JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:
> Fred Oberhauser (frob@mpi-sb.mpg.de) wrote:
> | In article <4ppd4l$qs8@silver.jba.co.uk> JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:
>
> | > Fred Oberhauser (frob@mpi-sb.mpg.de) wrote:
> | > | template <class T>
> | > | class Container {
> | > | public:
> | > | explicit T& operator[](int index); // proposed new syntax
> | > | };
> | > |
> | > | class Base { public:
> | > | inline virtual Method() {...}
> | > | };
> | > |
> | > | class Derived : public Base { public:
> | > | virtual Method();
> | > | }
> | > |
> | > | Container<Base> baseContainer;
> | > |
> | > | // because of T::T& instead of just T&, this call is non-virtual;
> | > | // call Base::Method():
> | > | baseContainer[4711].Method();
> | >
> | > What does this give us that we don't already have with the existing syntax ?
> | >
> | > baseContainer[4711].Base::Method() ;
> |
> | if the implementation of Base & Container<T>::operator[](...) wants to
> | return a (reference to an) object of type Derived, you could run into
> | problems with
> |
> | > baseContainer[4711].Base::Method() ;
> |
> | whereas with Base::Base & Container<T>::operator[](...) an attempt to
> | return Derived's will be an error.
>
> The requirement that you gave in your original example was that you wanted
> to avoid the virtual function call resolution, and existing syntax allows
> you to do this. Why is returning an object of class Derived a problem ?
> The very assumption behind your specification of always wanting to call
> Base::Method in the first place is that you may be returning objects of
> class Derived.
>
> So what, exactly, is the problem that you are trying to solve here ?
I don't have a problem I can't express in C++ as it is, but that
could easily be expressed much more straightforward (without need for run
time type checking and scope resolution):
I would like to specify, that a pointer or a reference has it's
declared type as dynamic type, such that:
* it's possible to check at compile time that the implementation of
ReferenceToDynamicType T Container<T>::operator(int index) // (*)
is only correct if it returns a T and nothing derived from it. As a
consequence
* the compiler may freely inline any calls to objects returned by (*),
since their dynamic type is known to be the declared pointed-to type.
* so novice users don't have to fiddle with scope resolution if they
need fast code (and as far as i was told the speed comes from otimizing
the inlined code, not from the lack of a ponter-dereference or two) AND
* expert users are prevented from shooting themselves in the foot by
using accidentially the scope resolution operator when the referenced
type is in fact derived and virtual function calling is needed.
* sure they could do (run time!) typechecking:
Base& tmp = expression;
assert(typeid(tmp) == typeid(Base));
temp.Base::Method();
but: why force experts to do additional type checking if the compiler
can do it for you, provided you may give him the necessary hints?
and: why use rtti if typechecking could easily be done at compile time?
* in the previos post i also gave an example how to use such an extension
usefully in parameters. There, too, it would make C++ both saver and
faster by avoiding the need for rtti and by enabling inline
optimization.
In my eyes this would be helpful to faster and saver produce faster and
saver code for both novice and adept C++ users.
fred oberhauser
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/07/03 Raw View
In article <4r0bg3$fd5@silver.jba.co.uk> JdeBP@jba.co.uk (Jonathan de
Boyne Pollard) writes:
> Fred Oberhauser (frob@mpi-sb.mpg.de) wrote:
> | In article <4ppd4l$qs8@silver.jba.co.uk> JdeBP@jba.co.uk
> | (Jonathan de Boyne Pollard) writes:
>
> | > Fred Oberhauser (frob@mpi-sb.mpg.de) wrote:
> | > | template <class T>
> | > | class Container {
> | > | public:
> | > | explicit T& operator[](int index); // proposed new syntax
> | > | };
> | > |
> | > | class Base { public:
> | > | inline virtual Method() {...}
> | > | };
> | > |
> | > | class Derived : public Base { public:
> | > | virtual Method();
> | > | }
> | > |
> | > | Container<Base> baseContainer;
> | > |
> | > | // because of T::T& instead of just T&, this call is
> | > | // non-virtual;
> | > | // call Base::Method():
> | > | baseContainer[4711].Method();
> | >
> | > What does this give us that we don't already have with the existing
> | > syntax ?
> | >
> | > baseContainer[4711].Base::Method() ;
> |
> | if the implementation of Base & Container<T>::operator[](...) wants to
> | return a (reference to an) object of type Derived, you could run into
> | problems with
> |
> | > baseContainer[4711].Base::Method() ;
> |
> | whereas with Base::Base & Container<T>::operator[](...) an attempt to
> | return Derived's will be an error.
>
> The requirement that you gave in your original example was that you wanted
> to avoid the virtual function call resolution, and existing syntax allows
> you to do this. Why is returning an object of class Derived a problem ?
> The very assumption behind your specification of always wanting to call
> Base::Method in the first place is that you may be returning objects of
> class Derived.
>
> So what, exactly, is the problem that you are trying to solve here ?
I don't have a problem I can't express in C++ as it is, but that
could easily be expressed much more straightforward (without need for run
time type checking and scope resolution):
I would like to specify, that a pointer or a reference has it's
declared type as dynamic type, such that:
* it's possible to check at compile time that the implementation of
ReferenceToDynamicType T Container<T>::operator(int index) // (*)
is only correct if it returns a T and nothing derived from it. As a
consequence
* the compiler may freely inline any calls to objects returned by (*),
since their dynamic type is known to be the declared pointed-to type.
* so novice users don't have to fiddle with scope resolution if they
need fast code (and as far as i was told the speed comes from otimizing
the inlined code, not from the lack of a ponter-dereference or two) AND
* expert users are prevented from shooting themselves in the foot by
using accidentially the scope resolution operator when the referenced
type is in fact derived and virtual function calling is needed.
* sure they could do (run time!) typechecking:
Base& tmp = expression;
assert(typeid(tmp) == typeid(Base));
temp.Base::Method();
but: why force experts to do additional type checking if the compiler
can do it for you, provided you may give him the necessary hints?
and: why use rtti if typechecking could easily be done at compile time?
* in the previos post i also gave an example how to use such an extension
usefully in parameters. There, too, it would make C++ both saver and
faster by avoiding the need for rtti and by enabling inline
optimization.
In my eyes this would be helpful to faster and saver produce faster and
saver code for both novice and adept C++ users.
fred oberhauser
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: martelli@cadlab.it (Alex Martelli)
Date: 1996/06/27 Raw View
ccwf@thales.klab.caltech.edu (Charles Fu) writes:
...
>It might also be interesting to allow each virtual member function to
>specify that it can/cannot be overridden in a derived class. I don't
>know of any examples from other languages for such a fine-grained
>mechanism and don't know all the repercussions.
Doesn't Java do exactly this with one of its semantic overloads for
its keyword "final"?
Alex
--
DISCLAIMER: these are TOTALLY personal opinions and viewpoints, NOT connected
in any way with my employer, nor any other organization or individual!
Email: martelli@cadlab.it Phone: +39 (51) 597313
CAD.LAB s.p.a., v. Ronzani 7/29, Casalecchio, Italia Fax: +39 (51) 597120
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 1996/06/28 Raw View
Fred Oberhauser (frob@mpi-sb.mpg.de) wrote:
| In article <4ppd4l$qs8@silver.jba.co.uk> JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:
| > Fred Oberhauser (frob@mpi-sb.mpg.de) wrote:
| > | template <class T>
| > | class Container {
| > | public:
| > | explicit T& operator[](int index); // proposed new syntax
| > | };
| > |
| > | class Base { public:
| > | inline virtual Method() {...}
| > | };
| > |
| > | class Derived : public Base { public:
| > | virtual Method();
| > | }
| > |
| > | Container<Base> baseContainer;
| > |
| > | // because of T::T& instead of just T&, this call is non-virtual;
| > | // call Base::Method():
| > | baseContainer[4711].Method();
| >
| > What does this give us that we don't already have with the existing syntax ?
| >
| > baseContainer[4711].Base::Method() ;
|
| if the implementation of Base & Container<T>::operator[](...) wants to
| return a (reference to an) object of type Derived, you could run into
| problems with
|
| > baseContainer[4711].Base::Method() ;
|
| whereas with Base::Base & Container<T>::operator[](...) an attempt to
| return Derived's will be an error.
The requirement that you gave in your original example was that you wanted
to avoid the virtual function call resolution, and existing syntax allows
you to do this. Why is returning an object of class Derived a problem ?
The very assumption behind your specification of always wanting to call
Base::Method in the first place is that you may be returning objects of
class Derived.
So what, exactly, is the problem that you are trying to solve here ?
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/06/17 Raw View
In article <4ppd4l$qs8@silver.jba.co.uk> JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:
> Fred Oberhauser (frob@mpi-sb.mpg.de) wrote:
> | template <class T>
> | class Container {
> | public:
> | T::T& operator[](int index); // "T::T&" is proposed new syntax
could also be some `explicit T &'...
> | };
> |
> | class Base { public:
> | inline virtual Method() {...}
> | };
> |
> | class Derived : public Base { public:
> | virtual Method();
> | }
> |
> | Container<Base> baseContainer;
> |
> | // because of T::T& instead of just T&, this call is non-virtual;
> | // call Base::Method():
> | baseContainer[4711].Method();
>
> What does this give us that we don't already have with the existing syntax ?
>
> baseContainer[4711].Base::Method() ;
it gives us
1. more efficient and safer libraries for non-expert users, since they
won't have to fiddle with scope resolution in the case of return types
(like the example above) and with type checking (see next section)
2. static type checking for pointer/reference return types:
if the implementation of Base & Container<T>::operator[](...) wants to
return a (reference to an) object of type Derived, you could run into
problems with
> baseContainer[4711].Base::Method() ;
whereas with Base::Base & Container<T>::operator[](...) an attempt to
return Derived's will be an error.
So you should assert the correct type first:
Base & temp = baseContainer[4711];
assert(typeid(temp) == typeid(Base));
temp.Base::Method();
which is not really simple and easy-to-use...
3. static type checking for pointer/reference parameters in cases like
this one:
I continue the example from previous post above. Take some function
which, for the sake to avoid copying, uses a Base ptr/reference type
argument. Then there are two possibilities to express, that you want
not to give it a Derived type argument:
#if 0
// the current state of affairs:
template <class T>
void SomeFunction(T const & arg);
#endif
// new proposition:
void SomeFunction(Base::Base const & arg);
Base b;
SomeFunction(b);
now if we take the current state of C++, we can implement SomeFunction
like this:
template <class T>
void SomeFunction(T const & arg)
{
// enforce typechecking;
// errors will be detected at run-time, not at
// compile-time...
assert(typeid(arg) == typeid(Base));
...
// call the Method appropriate for arg
arg.Base::Method();
}
if C++ was enriched with the possibility to declare a ptr/reference to
mean the declared type and no type derived from it, this would
simplify the example like this:
void SomeFunction(Base::Base const & arg)
{
...
arg.Method();
}
Here, the typechecking will be done at compile time, and you don't have to
use Scope resolution, since arg is of type Base, and not of any type
Derived.
So, indeed, after some fruitfull discussion in this group I've
learned, that there is no real _need_ for my proposition since
everything can be expressed in C++ as it is; nevertheless, it could
make libraries faster and easyer and safer to use, since typechecking
could then be done at compile-time, with sake for inlining and
type-safety.
fred
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Raja R Harinath <harinath@cs.umn.edu>
Date: 1996/06/09 Raw View
lippin@svcdudes.com (Tom Lippincott) writes:
> This brings up an interesting point: if assertions were a proper part of
> the language, a darned smart compiler could find this optimization by
> itself. I imagine an assert-statement:
>
> assert expression;
>
> with five rules:
>
> 1. The expression must have type (or be convertible to) bool.
> 2. Whether or not the expression is evaluated is implementation dependent.
> 3. If the expression is unevaluated, the assert-statement has no effect.
> 4. If the expression evaluates to true, the assert-statement has no
> further effect.
> 5. If the expression evaluates to false, the assert-statement has
> implementation dependent behavior.
Since `assert' is part of the standard library, it is pretty much a
proper part of the language. That means that compilers can derive any
amount of information reasonable from the use of `assert' in the absence
of NDEBUG (all this smarts being turned on by the `#include <cassert>'
statement.)
In effect, you don't need an `assert-statement'.
- Hari
--
Raja R Harinath ------------------------------ harinath@cs.umn.edu
"When all else fails, read the instructions." -- Cahn's Axiom
"Our policy is, when in doubt, do the right thing." -- Roy L Ash
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/06/10 Raw View
In article <4p8ci8$m5f@uruguay.it.earthlink.net> scotty@cinenet.net (J Scott Peter XXXIII i/iii) writes:
> >if this is inside a function body, and obj is a Base&
> >
> > void Foo(Base& obj) {
> > obj.Base::fn();
> > }
> >
> >you cant't be sure that Foo will be called with arguments of type Base:
> >
> > Derived d;
> > Foo(d);
> >
> >is valid, but probably will produce unexpected behaviour. My question
> >is, if it would be much effort to add some syntactic sugar to C++, to
> >make the intent only to pass objects of static type Base to Foo clear
> >at compile-time.
> >
> >Declare Foo as
> >
some means to express this reference/pointer means an object of type
Base and nothing derived from it:
> > void Foo(Base::Base& b);
> >or void Foo(explicit Base & b);
> >
> >
> >This could then generate a compile-time error in
> >
> > Derived d;
> > Foo(d);
> >
> >and would not require the Base:: qualification in the call to fn
> >inside the body of Foo
> >
> > {
> > // this will allways call Base::fn
> > obj.fn();
> > }
> >
>
[snippedysnip]
> struct A {
> virtual int f() = 0; // Abstract class.
> };
>
> struct B: A {
> inline void f() { return 1; } // Quick execution.
> };
>
> struct C: A {
> inline void f() { return 2; } // Likewise.
> }
>
> Now declare a template that takes an A* or descendent:
>
> template<class T> void test(T* t)
> {
> for (int i = 0; i < 1000000; i++)
> something += t->f(); // Call f() a lot
> }
>
> This version of test works with an A*, B* or C*. But execution is slow
> because every call to f is virtual. We want that for A, but would like it to
> be inline for B and C. So let's try to speed it up:
>
> template<class T> void test(T* t)
> {
> for (int i = 0; i < 1000000; i++)
> something += t->T::f(); // Call f() a lot
> }
>
> Now we're calling the inline versions, right? But now test() will not take
> an A*, because calling t->A::f() invokes a pure virtual function. So you
> can't win! (Note, Fred, that your idea of declaring argument t as T::T* t
> would fail for the same reason.)
>
this template trick will help for parameters! thanks!
by the way this does not conflict with the idea to express that a
pointer will point to an object with equal dynamic and static type:
it is never possible to instantiate an object of an abstract type, so
`explicit A*'or `A::A*' (or however one would realize such a concept) will
be an error :-)
nevertheless there is no solution for pointers/references as return
value of a function: for containers, it is often usefull to overload
`operator[](IndexType index)'.
now imagine such a container:
template <class T>
struct Container {
T& operator[](Index i);
};
struct Base {
// some short ideal candidate for inlining
virtual void Method() { ... }
};
struct Derived : Base {
void Method() { ... }
};
Container<Base> baseContainer;
...
Base& baseObject = baseContainer[...];
for (a zillion times)
baseObject.Method();
in this case, it is not possible to see that Container<Base> will give
you an object of dynamic type Base as return value of operator[]. so,
either, you say
for (a zillion times)
baseObject.Base::Method();
at your own risk, or you have some means to express, that operator[]
will allways return objects of dynamic type Base. again, add a
keyword, or some additional meaning to the scope resolution operator
(for example):
template <class T>
struct Container {
T::T& operator[](Index i);
};
then, in the definition of operator[], returning a value of type Derived
will cause a compile error, and in the for loop, inlining can be
performed without further hints from the user, which allows for
easy-to-use highly efficient container classes...
[snippedy snip]
fred
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: ccwf@thales.klab.caltech.edu (Charles Fu)
Date: 1996/06/12 Raw View
This thread has primarily addressed the issue of allowing functions to
specify that parameters are of a specified dynamic type (or, more
generally, of letting the compiler know that certain references and
pointers have a specified dynamic type).
A related issue that may be worth pursuing is allowing classes to
specify, in their declaration, that no other classes derive from them.
(Making ctors/dtors private would prevent inheritance, but would also
greatly hinder use.) Such an extension would be useful because all
references and pointers to such a class have known dynamic type, so
the compiler can avoid dynamic dispatch and can potentially inline all
virtual member functions of such a class. Obviously, a compiler error
would be generated if a user attempts to derive from a class declared
to be "non-inheritable." Using non-inheritable classes should also be
much safer than allowing programmers to specify an explicit dynamic
type for all parameters, or all references and pointers (which would
be useful, nonetheless). Eiffel is an example of prior art for a
mechanism much like this.
(For systems with smart linkers capable of doing cross-module inlining
and other optimizations, it should be possible to determine at link
time whether or not a class has any derived classes, making an
explicit mechanism unnecessary and undesirable. However, few present
systems are capable of link-time optimizations, and I am unaware of
any that do perform this particular optimization.)
It might also be interesting to allow each virtual member function to
specify that it can/cannot be overridden in a derived class. I don't
know of any examples from other languages for such a fine-grained
mechanism and don't know all the repercussions.
-ccwf
===============================================================================
,-- Charles C. Fu
___ __ __. . ,-/-- V.P. / Bacchus, Inc. ccwf@bacchus.com
(_,(_,|/|/ / V.P. / FTL Enterprises, Inc. (818)442-0897
--' http://www.klab.caltech.edu/~ccwf ccwf@klab.caltech.edu
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: anhaeupl@late.e-technik.uni-erlangen.de (Bernd Anhaeupl)
Date: 1996/06/12 Raw View
In article <4pl4rn$gpn@gap.cco.caltech.edu> ccwf@thales.klab.caltech.edu (Charles Fu) writes:
This thread has primarily addressed the issue of allowing functions to
specify that parameters are of a specified dynamic type (or, more
generally, of letting the compiler know that certain references and
pointers have a specified dynamic type).
A related issue that may be worth pursuing is allowing classes to
specify, in their declaration, that no other classes derive from them.
(Making ctors/dtors private would prevent inheritance, but would also
greatly hinder use.) Such an extension would be useful because all
references and pointers to such a class have known dynamic type, so
the compiler can avoid dynamic dispatch and can potentially inline all
virtual member functions of such a class.
....
It might also be interesting to allow each virtual member function to
specify that it can/cannot be overridden in a derived class. I don't
know of any examples from other languages for such a fine-grained
mechanism and don't know all the repercussions.
Both ideas do not allow overriding a virtual member function of
an (abstract) base class by non-virtual member functions in derived
classes, which would also solve the problems:
class base
{
virtual void f()=0;
}
class derived1
{
void f(){....}; /* designed to handle also
f() of all classes derived
from derived1 */
}
class derived2
{
void f(){....}; /* designed to handle also
f() of all classes derived
from derived2 */
}
class derived12
{
void f(){....}; /* (only) run-time optimized
version of derived1::f();
otherwise identical to
derived1::f() */
}
base * baseptr;
derived1 * derived1ptr
derived12 * derived12ptr;
derived2 * derived2ptr;
where
baseptr->f() should be resolved virtual;
(including calls to derived12::f());
derived1ptr->f() should always call derived1::f();
derived2ptr->f() should always call derived2::f();
derived12ptr->f() should always call derived12::f();
or in other words, I would only like virtual resolution of a member
function call, iff the final overrider of the __declared__ class
type is explicitly marked virtual. This is similar to Scott's
(scotty@cinenet.net) proposal for that topic some days ago in this
group.
--
Bernd Anhaeupl Tel.: +49 9131 857787
LATE - Uni Erlangen
Cauerstr. 7 Email: anhaeupl@late.e-technik.uni-erlangen.de
91058 Erlangen
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/06/13 Raw View
IMHO, to declare ptr/references to mean the declared type, and no type
derived from it is not related to control wether or not a virtual
function should remain virtual:
again I'll illustrate this with my Container example:
template <class T>
// this Container will only contain objects of type T, and no
// derived classes
class Container {
public:
// `T::T' is some syntactic sugar to express:
// a reference or pointer to this type will certainly be
// initialized with an object of type T
// it could also be `explicit T', for example
T::T& operator[](int index);
};
class Base {
public:
inline
virtual Method() {...}
};
class Derived : public Base {
public:
virtual Method();
}
Container<Base> baseContainer;
...
// bool Dice::operator()() gives you a random boolean value
Base* basePtr = (dice())? new Derived() : new Base();
// virtual function call, usefull and wanted
basePtr->Method();
// beacuse of T::T& instead of just T&, this call is non-virtual;
// call Base::Method():
baseContainer[4711].Method();
so i hope this makes visible, why declaring a reference to mean the
static type is not related to removing the virtuality-gene in the
middle of an inheritance tree.
fred
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 1996/06/13 Raw View
Fred Oberhauser (frob@mpi-sb.mpg.de) wrote:
| template <class T>
| class Container {
| public:
| T::T& operator[](int index); // "T::T&" is proposed new syntax
| };
|
| class Base { public:
| inline virtual Method() {...}
| };
|
| class Derived : public Base { public:
| virtual Method();
| }
|
| Container<Base> baseContainer;
|
| // because of T::T& instead of just T&, this call is non-virtual;
| // call Base::Method():
| baseContainer[4711].Method();
What does this give us that we don't already have with the existing syntax ?
baseContainer[4711].Base::Method() ;
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: ulf121061@aol.com (Ulf121061)
Date: 1996/06/03 Raw View
>frob@mpi-sb.mpg.de (Fred Oberhauser) wrote:
>neither ARM nor D&E nor FAQ did answer me this:
>
>is there a way to express, that a reference or a pointer is only
>allowed to mean objects of its static type, such that inline
>optimization can safely be done?
>
> [Moderator's note: nope, C++ doesn't have any direct support
> for this. -fjh.]
>
>this would be usefull in the use of homogenous containers,
>which give you an object via a reference/pointer:
>
> template <class T>
> class Container {
> public:
> T& operator[](int index);
> };
>
> class Foo {
> // big data structure
> public:
> virtual void Moo();
> };
>
> inline void Foo::Moo() {... }
>
> Container<Foo> foo;
>
> foo[4711].Moo(); // <- inline expand this call
>
>it would also allow for inline-expansion in this case:
> void WorkOnFoo( Foo& foo )
> {
> for (... a gazillion times ...)
> foo.Moo(); // could be expanded, if we could assert, that
> // WorkOnFoo will only be called for objects
> // of dynamic type Foo
> }
Your example should compile and run. But your Moo() will not expanded
inline, because it is
defined virtual and called in an polymorphic way via ptr or reference.
Make the Moo() non virtual and it will be expanded inline. When it ist
really true, that, e.g,
WorkOnFoo() will only be called for objects of dynamic type Foo, then it
is not necessary
to make Moo() virtual.
An longer and better explain is in Scott Meyers, Effectiv C++, chapter 33
and chapters 35 ff.
Hope I could help,
Ulf
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/06/04 Raw View
In article <4on8do$90q@mulga.cs.mu.OZ.AU> fjh@mundook.cs.mu.OZ.AU
(Fergus Henderson) writes:
> maxtal@suphys.physics.su.oz.au (John Skaller) writes:
>
> >frob@mpi-sb.mpg.de (Fred Oberhauser) wrote:
> >
> >>neither ARM nor D&E nor FAQ did answer me this:
> >
> >>is there a way to express, that a reference or a pointer is only
> >>allowed to mean objects of its static type, such that inline
> >>optimization can safely be done?
> >
> >> [Moderator's note: nope, C++ doesn't have any direct support
> >> for this. -fjh.]
...yet ;-)
> >
> > How about an array of one element?
>
> Well, that's certainly a nice try. But it won't work for parameters,
> because an formal parameter of array type is treated as syntactic sugar
> for a pointer. You would have to use a reference or pointer to an
> array, rather than an array.
for parameters, it does work. For a return type, it can't, since
arrays are not allowed as return type of a function:
#include <iostream.h>
class Base {
public:
virtual void Print() {cout << "Base\n";}
};
class Derived
: public Base {
public:
void Print() {cout << "Derived\n";}
};
typedef Base baseArray[1];
typedef Derived derivedArray[1];
baseArray base;
derivedArray derived;
void BaseNotDerivedAsParameter(baseArray& arg) {
arg[0].Print();
}
#if 0
// not allowed:
// `returnArray' declared as function returning an array
baseArray ReturnBaseNotDerived() {
return base;
}
#endif
int main(void) {
BaseNotDerivedAsParameter(base);
// using `base' or `derived' requires
// inconvenient array dereference:
derived[0].Print();
#if 0
// not allowed: Derived[1] as argument of type baseArray&
BaseNotDerivedAsParameter(derived);
// thus one could use the array as return parameter
ReturnBaseNotDerived()[0].Print();
#endif
}
So one has a workaround for a lacking C++-feature... nevertheless,
wouldn't it be nice to have a simpler syntax for the behaviour desired?
#include <iostream.h>
class Base {
public:
virtual void Print() {cout << "Base\n";}
};
class Derived
: public Base {
public:
void Print() {cout << "Derived\n";}
};
Base base;
Derived derived;
void BaseNotDerivedAsParameter(Base::Base& arg) {
// no virtual function call, will allways be Base
arg.Print();
}
// now, returning the thing i wish also works
Base::Base & ReturnBaseNotDerived() {
return base;
}
int main(void) {
BaseNotDerivedAsParameter(base);
derived.Print();
#if 0
// not allowed: Derived as argument of type Base&
arrayAsParameter(derived);
#endif
ReturnBaseNotDerived()[0].Print();
}
fred
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/06/04 Raw View
In article <4ot810$91c@newsbf02.news.aol.com> ulf121061@aol.com
(Ulf121061) writes:
> >frob@mpi-sb.mpg.de (Fred Oberhauser) wrote:
> >neither ARM nor D&E nor FAQ did answer me this:
> >
> >is there a way to express, that a reference or a pointer is only
> >allowed to mean objects of its static type, such that inline
> >optimization can safely be done?
> >
> > [Moderator's note: nope, C++ doesn't have any direct support
> > for this. -fjh.]
> >
> >this would be usefull in the use of homogenous containers,
> >which give you an object via a reference/pointer:
> >
> > template <class T>
> > class Container {
> > public:
> > T& operator[](int index);
> > };
> >
> > class Foo {
> > // big data structure
> > public:
> > virtual void Moo();
> > };
> >
> > inline void Foo::Moo() {... }
> >
> > Container<Foo> foo;
> >
> > foo[4711].Moo(); // <- inline expand this call
> >
> >it would also allow for inline-expansion in this case:
> > void WorkOnFoo( Foo& foo )
> > {
> > for (... a gazillion times ...)
> > foo.Moo(); // could be expanded, if we could assert, that
> > // WorkOnFoo will only be called for objects
> > // of dynamic type Foo
> > }
>
> Your example should compile and run. But your Moo() will not expanded
> inline, because it is
> defined virtual and called in an polymorphic way via ptr or reference.
> Make the Moo() non virtual and it will be expanded inline. When it ist
> really true, that, e.g,
> WorkOnFoo() will only be called for objects of dynamic type Foo, then it
> is not necessary
> to make Moo() virtual.
>
This is the well known current state of affairs. If there would be
some additional sugar to C++ to express that a pointer or reference
means the static type given in the declaration, the optimizations
would be valid, and misuses of the conatiner would be detected at
compile time:
// assume this to be the way to express
// `don't accept derived classes':
// T::T &
template <class T>
class Container {
public:
T::T& operator[](int index);
};
class Base {
// big data structure
public:
virtual void DoIt();
};
class Derived : public Base {
public:
void DoIt();
};
inline void Base::DoIt() {... }
Container<Base> baseContainer;
baseContainer[4711].DoIt(); // <- inline expand this call
Derived derived;
#if error
// this is not allowed:
baseContainer[0815] = derived;
#endif
it would also allow for inline-expansion in this case:
void WorkOnBase(Base::Base& base )
{
for (... a gazillion times ...)
base.DoIt(); // can be expanded, since base
// will certainly be of dynamic type Base
}
since this would not be allowed:
#if error
WorkOnFoo(derived);
#endif
> Hope I could help,
>
> Ulf
Hope I could make the question more visible.
fred
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/06/04 Raw View
In article <u94towbbon.fsf@yorick.cygnus.com> jason@cygnus.com (Jason
Merrill) writes:
> Why not just use obj.Base::fn (), which already means "suppress
> polymorphism for this call"?
>
> Jason
if this is inside a function body, and obj is a Base&
void Foo(Base& obj) {
obj.Base::fn();
}
you cant't be sure that Foo will be called with arguments of type Base:
Derived d;
Foo(d);
is valid, but probably will produce unexpected behaviour. My question
is, if it would be much effort to add some syntactic sugar to C++, to
make the intent only to pass objects of static type Base to Foo clear
at compile-time.
Declare Foo as
void Foo(Base::Base& b);
or void Foo(explicit Base & b);
This could then generate a compile-time error in
Derived d;
Foo(d);
and would not require the Base:: qualification in the call to fn
inside the body of Foo
{
// this will allways call Base::fn
obj.fn();
}
fred
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: lippin@svcdudes.com (Tom Lippincott)
Date: 1996/06/05 Raw View
> frob@mpi-sb.mpg.de (Fred Oberhauser) wrote:
> neither ARM nor D&E nor FAQ did answer me this:
>
> is there a way to express, that a reference or a pointer is only
> allowed to mean objects of its static type, such that inline
> optimization can safely be done?
...
> it would also allow for inline-expansion in this case:
> void WorkOnFoo( Foo& foo )
> {
> for (... a gazillion times ...)
> foo.Moo(); // could be expanded, if we could assert, that
> // WorkOnFoo will only be called for objects
> // of dynamic type Foo
> }
It's not quite what you asked for, but you could use an explicit qualifier
on the call you want inlined:
void WorkOnFoo( Foo& foo )
{
assert( typeid( foo ) == typeid( Foo ) );
for (... a gazillion times ...)
foo.Foo::Moo();
}
This brings up an interesting point: if assertions were a proper part of
the language, a darned smart compiler could find this optimization by
itself. I imagine an assert-statement:
assert expression;
with five rules:
1. The expression must have type (or be convertible to) bool.
2. Whether or not the expression is evaluated is implementation dependent.
3. If the expression is unevaluated, the assert-statement has no effect.
4. If the expression evaluates to true, the assert-statement has no
further effect.
5. If the expression evaluates to false, the assert-statement has
implementation dependent behavior.
Most compilers would presumably have options for "check every assertion,"
"check no assertions," and "check the assertions I like."
Rule 3 would force a compiler to write code which checks any assertion it
takes advantage of; I'm not sure that's the right way to go.
I think this would open a wide door for optimizations which few compiler
writers would step through. What do other people think?
--Tom Lippincott
lippin@svcdudes.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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: scotty@cinenet.net (J Scott Peter XXXIII i/iii)
Date: 1996/06/07 Raw View
In article <2xloi3sxbh.fsf@mpii01106.mpi-sb.mpg.de>, frob@mpi-sb.mpg.de (Fred
Oberhauser)
<4og4e4$p9a@reader1.reader.news.ozemail.net>
<4on8do$90q@mulga.cs.mu.OZ.AU> <u94towbbon.fsf@yorick.cygnus.com>
wrote:
>if this is inside a function body, and obj is a Base&
>
> void Foo(Base& obj) {
> obj.Base::fn();
> }
>
>you cant't be sure that Foo will be called with arguments of type Base:
>
> Derived d;
> Foo(d);
>
>is valid, but probably will produce unexpected behaviour. My question
>is, if it would be much effort to add some syntactic sugar to C++, to
>make the intent only to pass objects of static type Base to Foo clear
>at compile-time.
>
>Declare Foo as
>
> void Foo(Base::Base& b);
>or void Foo(explicit Base & b);
>
>
>This could then generate a compile-time error in
>
> Derived d;
> Foo(d);
>
>and would not require the Base:: qualification in the call to fn
>inside the body of Foo
>
> {
> // this will allways call Base::fn
> obj.fn();
> }
>
Actually, there is a cleaner language fix that I have been longing for. What
is needed is a way to specify how far down a class's descendent tree a
function remains virtual.
struct A {
void f(); // Called staticly.
virtual void g(); // Called dynamically.
};
The keyword "virtual" (a horrible choice of term, by the way) means "my
descendents might override me", and therefore always resolve a call to g()
dynamically. When the virtual keyword is omitted, it means "I'm the end of
the line as far as this function goes", so always call f() staticly.
struct B: A {
void g(); // B's override of A::g.
};
Here, B overrides, or implements, A::g. However, there is no way for B to
indicate whether *its* g() should be called dynamically or staticly (when
called via an explicit pointer to B). B should in turn be able to say either
"I'm the end of the line", or "My children might override me", regarding g().
If B::g is not the end of the line, B should have to "revirtualise" g():
struct B: A {
virtual void g(); // Override A::g, and revirtualise it.
};
Otherwise, a call to g() through a B* should always call B::g() staticly.
Why is this necessary? Why not just specify either b->g() or b->B::g() as
needed? Look at this example:
struct A {
virtual int f() = 0; // Abstract class.
};
struct B: A {
inline void f() { return 1; } // Quick execution.
};
struct C: A {
inline void f() { return 2; } // Likewise.
}
Now declare a template that takes an A* or descendent:
template<class T> void test(T* t)
{
for (int i = 0; i < 1000000; i++)
something += t->f(); // Call f() a lot
}
This version of test works with an A*, B* or C*. But execution is slow
because every call to f is virtual. We want that for A, but would like it to
be inline for B and C. So let's try to speed it up:
template<class T> void test(T* t)
{
for (int i = 0; i < 1000000; i++)
something += t->T::f(); // Call f() a lot
}
Now we're calling the inline versions, right? But now test() will not take
an A*, because calling t->A::f() invokes a pure virtual function. So you
can't win! (Note, Fred, that your idea of declaring argument t as T::T* t
would fail for the same reason.)
That's why it's necessary to add a feature that allows *each* class in an
inheritance hierarchy to decide whether its version of a function is virtual
or not.
J Scott Peter XXXIII i/iii // Wrong thinking is punishable.
Venice, CA, USA // Right thinking will be as quickly rewarded.
// You will find it an effective combination.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: maxtal@suphys.physics.su.oz.au (John Skaller)
Date: 1996/05/29 Raw View
frob@mpi-sb.mpg.de (Fred Oberhauser) wrote:
>neither ARM nor D&E nor FAQ did answer me this:
>is there a way to express, that a reference or a pointer is only
>allowed to mean objects of its static type, such that inline
>optimization can safely be done?
> [Moderator's note: nope, C++ doesn't have any direct support
> for this. -fjh.]
How about an array of one element?
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/05/31 Raw View
maxtal@suphys.physics.su.oz.au (John Skaller) writes:
>frob@mpi-sb.mpg.de (Fred Oberhauser) wrote:
>
>>neither ARM nor D&E nor FAQ did answer me this:
>
>>is there a way to express, that a reference or a pointer is only
>>allowed to mean objects of its static type, such that inline
>>optimization can safely be done?
>
>> [Moderator's note: nope, C++ doesn't have any direct support
>> for this. -fjh.]
>
> How about an array of one element?
Well, that's certainly a nice try. But it won't work for parameters,
because an formal parameter of array type is treated as syntactic sugar
for a pointer. You would have to use a reference or pointer to an
array, rather than an array.
Even then, I'm still not yet 100% convinced that a compiler can
assume that it really does point to an array of objects of its static type.
Are you sure that a programmer can't cast a `Base*' pointer which
points to a single derived object to `Base(*)[1]', i.e. a pointer to an
array [1] of Base? For example, the following program compiles fine
on a couple of C++ compilers I tried, and prints out `Derived'.
#include <stdio.h>
struct Base { virtual void f() { printf("Base\n"); } };
struct Derived : Base { virtual void f() { printf("Derived\n"); } };
void g(Base(&x)[1]) {
x->f();
}
int main() {
Base *pointer = new Derived;
void *raw_memory = pointer;
Base (*array)[1] = static_cast<Base (*)[1]>(raw_memory);
g(*array);
return 0;
}
If the optimization you're suggesting is valid, then the compiler could
assume that the pointer passed to g() really must point to a base
class object and use static dispatch rather than dynamic dispatch
for the function call, resulting in a program that prints out `Base'
rather `Derived'.
Does the static_cast and the later dereference of the resulting pointer
in g() really cause undefined behaviour?
It may be so, but I wouldn't bet on it. In any case I think it might
require a non-trivial amount of bravery on the part of the compiler
vendor to implement this optimization.
--
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.
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: jason@cygnus.com (Jason Merrill)
Date: 1996/06/01 Raw View
Why not just use obj.Base::fn (), which already means "suppress
polymorphism for this call"?
Jason
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: frob@mpi-sb.mpg.de (Fred Oberhauser)
Date: 1996/05/24 Raw View
neither ARM nor D&E nor FAQ did answer me this:
is there a way to express, that a reference or a pointer is only
allowed to mean objects of its static type, such that inline
optimization can safely be done?
[Moderator's note: nope, C++ doesn't have any direct support
for this. -fjh.]
this would be usefull in the use of homogenous containers,
which give you an object via a reference/pointer:
template <class T>
class Container {
public:
T& operator[](int index);
};
class Foo {
// big data structure
public:
virtual void Moo();
};
inline void Foo::Moo() {... }
...
Container<Foo> foo;
...
foo[4711].Moo(); // <- inline expand this call
it would also allow for inline-expansion in this case:
void WorkOnFoo( Foo& foo )
{
...
for (... a gazillion times ...)
foo.Moo(); // could be expanded, if we could assert, that
// WorkOnFoo will only be called for objects
// of dynamic type Foo
...
}
thanks
fred
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]