Topic: Static cast "across" a hierarchy
Author: "Alex Martelli" <alex@magenta.com>
Date: 1999/02/02 Raw View
Jerry Leichter wrote in message <3693BFBD.2585@smarts.com>...
[snip]
> p* pp = new p;
> q* qq = static_cast<q*>(static_cast<base*>(p));
>
> q->f(); // Legal?
>
>Can one guarantee, within the C++ standard, that the marked line will
>invoke p::f()? (It's certainly the case that, with the usual defini-
No, I don't think so; since the real type of what is being cast to q*
is NOT q*, therefore the static_cast introduces undefined behaviour.
It will never break in practice I guess (if p and q are defined in
practically identical ways like in your example), but surely a compiler
breaking it, albeit theoretical, would still be compliant.
E.g., a compiler, I believe, is allowed to implement pointers, not
just as addresses, but addresses+all sort of extra data for error
checking purposes; that might allow it to diagnose your illicit type
punning, and immediately halt the program with error diagnostics.
Alex
-----------== Posted via Newsfeeds.Com, Uncensored Usenet News ==----------
http://www.newsfeeds.com/ The Largest Usenet Servers in the World!
-----------== Over 66,000 Groups, Plus a Dedicated Binaries Server ==----------
[ 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: markw65@my-dejanews.com
Date: 1999/01/17 Raw View
In article <369CC02B.4567@smarts.com>,
Jerry Leichter <leichter@smarts.com> wrote:
> | Again, are we interested in what we can get away with on real systems
> | or conforming to the standard?
>
> It would have been *nice* if someone could come up with an argument from
> the standard that showed that this would work. (Certainly the defini-
> tion of static_cast alone doesn't guarantee this will work; nor does it
> forbid it.
The last sentence of 5.2.9.5 explicitly states that performing the cast itself
invokes undefined behaviour...
>I've seen enough unexpected derivations from the entire body
> of the standard to hold out some hope that that might the case here.)
>
> Failing that, it would be nice to hear, from those intimately involved
> in the process, that while not something guaranteed by the standard,
> this is within "the spirit of C++" and that it's like to work on any
> compiler accepted by the C++ community. Or alternatively that there are
> already compilers out there on which it *won't* work.
The compiler can assume that a virtual call through a pointer of type T must
go to an override in a class derived from T (as opposed to derived from a
base class of T). If link time analysis can determine that there are no
overrides (as in your example), it can change the virtual call to a direct
call. I only know of experimental compilers that do this. But it is a
relatively straightforward optimization to perform once you move away from
the traditional compile-then-link model. Something which compiler vendors
seem very reluctant to do.
Mark Williams
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own
---
[ 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: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/01/14 Raw View
On 14 Jan 99 09:38:36 GMT, Jerry Leichter <leichter@smarts.com> wrote:
>Failing that, it would be nice to hear, from those intimately involved
>in the process, that while not something guaranteed by the standard,
>this is within "the spirit of C++" and that it's like to work on any
>compiler accepted by the C++ community. Or alternatively that there are
>already compilers out there on which it *won't* work.
I just thought of something. Because the compiler says that the
static_cast from B to D is undefined in general, this means that
the implementation may do anything it wants. So an implementation
could do the following runtime check: If B is polymorphic,
silently replace the static_cast<D*>(base_ptr) with a
dynamic_cast<D*>(base_ptr). Now if base_ptr really points to an
object of ultimate or partial derived type D, then the
dynamic_cast works and the user never knows that the static_cast
was replaced by a dynamic_cast. If the dynamic_cast fails, then
the program may abort() or assert(false) with an appropriate error
message.
Of course, the compiler only does this check for downcasts, not
upcasts. Most importantly, the compiler does these checks when
you compile your program with debugging on. It removes the
checks when you compile with optimization. This runtime check
seems helpful to me, and it seems reasonable to believe that a
compiler will do it. So from this point of view, your code is
not acceptable.
Finally, a static_cast from A* to B* requires the definitions of
class A and class B to be seen. This way the compiler knows
whether they are related by inheritance or by conversion copy
constructors. By contrast, a dynamic_cast from A* to B* does
not require the definitions of A and B to be seen. So, as far
as the static_cast is concerned, the compiler knows that A and
B are related by inheritance, and it knows whether the cast
from A* to B* is an upcast or a downcast.
--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.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 ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: AllanW@my-dejanews.com
Date: 1999/01/14 Raw View
In article <369B7E9E.150E@smarts.com>,
Jerry Leichter <leichter@smarts.com> wrote:
>
> | > struct base
> | > { virtual void f() = 0;
> | };
> | >
> | > struct p : public base
> | > { void f() { ... };
> | > };
> | >
> | > struct q : public base
> | > { void f() { ... };
> | > };
> | >
> | > ...
> | > p* pp = new p;
> | > q* qq = static_cast<q*>(static_cast<base*>(p));
> | >
> | > q->f(); // Legal?
No, not legal. When you perform a static_cast from a p* to a q*,
you are supposed to guarantee that the object being pointed to
really really is a q before using the pointer.
You have an easy alternative:
p* pp = new p;
base* qq = p;
// ...
qq->f(); // Legal, calls p::f() -- which seems to be your intent.
Note that this needs no casting of any kind.
> The same kinds of arguments apply if we posit that the *vtable pointer*
> is at different offsets in any two of base, p, and q: To find the
> vtable pointer, the compiled code would first have to find the dynamic
> type. So the type had better be at a fixed offset. But it seems
> unlikely that a compiler would do that - since *something* has to go at
> a fixed offset, why not put the vtable pointer there, and store the type
> in the vtable, thus saving space in every object?
The vtable is an implementation detail, intentionally hidden from user
code. The fact that you can peek into memory and find it does not
change this fact. You aren't even supposed to rely on the compiler
using the vtable to perform virtual calls; theoretically, any other
method that the compiler could choose is equally valid, although in
reality vtables are one of the few ways of accomplishing everything
that the C++ spec requires.
The "usual" argument, here, goes something like this: This isn't valid
standard C++. [This is a standards newsgroup, after all!] Even if it
happens to work on your current compiler, you can't know that it will
work on other compilers. Even if you don't care about other CPUs or
other brands of compiler, "other compilers" also includes the next
version of the same brand of compiler. You shouldn't do this.
To this, I'll add that this isn't the proper way to do the job. There
are some ideas that are always bad to use in real programs, even if
they are guaranteed to work (which the code above is NOT). Even if it
does happen to work, it is deliberately misusing a language feature.
The language already has better ways to do this, ways that ARE
guaranteed to work. Furthermore, the code will mislead some
maintenance programmer someday, operating under the (perfectly
reasonable) delusion that the code was competently written. And the
most likely target of this delusion is you, yourself, sometime in the
future.
---
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own
[ 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 <dtribble@technologist.com>
Date: 1999/01/13 Raw View
Jerry Leichter wrote:
> | > struct base
> | > { virtual void f() = 0;
> | };
> | >
> | > struct p : public base
> | > { void f() { ... };
> | > };
> | >
> | > struct q : public base
> | > { void f() { ... };
> | > };
> | >
> | > ...
> | > p* pp = new p;
> | > q* qq = static_cast<q*>(static_cast<base*>(p));
> | >
> | > q->f(); // Legal?
> | >
> >
> | > Can one guarantee, within the C++ standard, that the marked line
> | > will invoke p::f()?
> |
> David R Tribble wrote:
> | Nope. More about this later.
> |
> | The static_cast for qq is illegal; you can't use static_cast to cast
> | a base pointer to a derived pointer.
>
> This statement, *as you have made it*, is clearly false: static_cast
> can perform the inverse of any standard conversion. Converting a
> derived pointer to a base pointer is one of the standard conversions.
Yep, I was wrong.
> | Even if it could be done, you can't assume that _vtable entries
> | will match up amongst the derived classes of a base class. In your
> | example, which only has a single _vtable entry, it might very
> | well work (assuming you can convert a p* to a q*), but there's
> | no guarantee it will.
>
> Think about what that would imply for an implementation! Suppose that
> f's index in the vtables for p and q were different. Then when com-
> piling:
> base* b = function_returning_base*()
> b->f();
>
> the compiler would have to generate code to test, at run time, what
> b's dynamic type is, and where f is located in vtable's for objects of
> that type. Since the compiler can't even know how many classes might
> be derived from base, or what they might look like, we're not even
> talking about a simple in-line check - the code would have to deal
> with derived classes invented well after the code was compiled.
>
> Possible? Sure - languages like Smalltalk do this all the time, since
> they really *can* do fully-dynamic method lookup. Likely in a *C++*
> compiler? I doubt it.
And yet a conforming implementation could very well do this.
(Imagine a C++ interpreter that does virtual function lookup the
hard way.)
The original question asked what the standard had to say about type
punning between sibling derived types (i.e., what was conforming),
not what was most likely to work on real implementations. Based on
what the standard has to say about it, my original answer is correct:
there's no guarantee it will work.
> | Perhaps instead you could separate out the common portions of
> | classes p and q and make that an intermediate class, which is
> | inherited by p and q:
> |
> | class base { ... f(); ... };
> |
> | class pq: public base { ... common stuff, e.g., f() ... };
> |
> | class p: public pq { ... };
> | class q: public pq { ... };
>
> f() is *not* common - if it were, it would be defined in base!
Okay, wrong approach.
>...
> What I've considered doing instead is to have an asBasic() method in
> iextended; in extended_impl, it's defined to convert "this" as in my
> question to a basic_impl*. Granted, it doesn't conform to the spec.
> But has anyone seen (or imagined) a C++ compiler where it won't work.
Again, are we interested in what we can get away with on real systems
or conforming to the standard?
-- David R. Tribble, dtribble@technologist.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: nimel@my-dejanews.com
Date: 1999/01/13 Raw View
In article <369B7E9E.150E@smarts.com>,
Jerry Leichter <leichter@smarts.com> wrote:
>
> | > struct base
> | > { virtual void f() = 0;
> | };
> | >
> | > struct p : public base
> | > { void f() { ... };
> | > };
> | >
> | > struct q : public base
> | > { void f() { ... };
> | > };
> | >
> | > ...
> | > p* pp = new p;
> | > q* qq = static_cast<q*>(static_cast<base*>(p));
> | >
> | > q->f(); // Legal?
> | >
[stuff deleted]
> Think about what that would imply for an implementation! Suppose that
> f's index in the vtables for p and q were different. Then when com-
> piling:
>
> base* b = function_returning_base*()
> b->f();
>
> the compiler would have to generate code to test, at run time, what b's
> dynamic type is, and where f is located in vtable's for objects of that
> type. Since the compiler can't even know how many classes might be
> derived from base, or what they might look like, we're not even talking
> about a simple in-line check - the code would have to deal with derived
> classes invented well after the code was compiled.
>
> Possible? Sure - languages like Smalltalk do this all the time, since
> they really *can* do fully-dynamic method lookup. Likely in a *C++*
> compiler? I doubt it.
>
> The same kinds of arguments apply if we posit that the *vtable pointer*
> is at different offsets in any two of base, p, and q: To find the
> vtable pointer, the compiled code would first have to find the dynamic
> type. So the type had better be at a fixed offset. But it seems
> unlikely that a compiler would do that - since *something* has to go at
> a fixed offset, why not put the vtable pointer there, and store the type
> in the vtable, thus saving space in every object?
I would say it would be quite complicated to have an implementation
with the same vtbl index for the virtual member functions in the
base classes and the derived classes:
struct B1
{
virtual void f() { } // vtbl index 1
};
struct B2
{
virtual void g() { } // vtbl index 1
};
struct D1 : B1
{
virtual void f() { } // vtbl index 1
};
struct D2 : B1, B2
{
virtual void f() { } // 1 or 2?
virtual void g() { } // 1 or 2?
};
struct D3 : B2
{
virtual void g() { } // vtbl index 1
};
/Niklas Mellin
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own
---
[ 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: Jerry Leichter <leichter@smarts.com>
Date: 1999/01/13 Raw View
| I would say it would be quite complicated to have an implementation
| with the same vtbl index for the virtual member functions in the
| base classes and the derived classes [because of multiple inheritance]
The two issues are unrelated. If I inherit from two classes, my vtable
(logically, different implementations are possible) consists of *three*
different vtables: One for each base class, and one of its own. A
vtable index is (again, logically) an offset into exactly one of these
classes. The compiler must be able to determine uniquely where a given
virtual method comes from. The example you gave - in which a virtual
function was defined in both base classes, but not in the derived class
- is illegal, since a call to that function is ambiguous. The ambiguity
must be resolved *by the programmer*, after which there's no problem in
choosing an offset. The compiler then has to construct special vtables
for (at least one of) the two base classes, and some run-time adjustment
code. You can find a description of all this in the ARM, page 230.
The example discussed there looks very much like your example. Note
that f() has vtable index 0 in all three logical vtables. (Two the
logical vtables are physically implemented in a single table.)
-- Jerry
[ 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: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/01/14 Raw View
On 13 Jan 99 14:22:17 GMT, nimel@my-dejanews.com <nimel@my-dejanews.com> wrote:
>struct D2 : B1, B2
>{
> virtual void f() { } // 1 or 2?
> virtual void g() { } // 1 or 2?
>};
On typical implementations, struct D2 has two virtual pointers.
The reason why the index entries must match up is so that you
can call the virtual function seamlessly through a pointer to
a base object. Eg,
D2 d2;
B1 * b1=&d2;
b1->f(); // as B1::f() has index==1, so should D2 for f()
B2 * b2=&d2;
b2->g(); // as B2::g() has index==1, so should D2 for g()
This means that multiple inherited classes have multiple virtual
pointers. Of course, an implementation doesn't have to do things
this way, which is which Jerry's code is ill-formed. But in
practice, it is ok.
--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.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 ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Jerry Leichter <leichter@smarts.com>
Date: 1999/01/14 Raw View
| Again, are we interested in what we can get away with on real systems
| or conforming to the standard?
It would have been *nice* if someone could come up with an argument from
the standard that showed that this would work. (Certainly the defini-
tion of static_cast alone doesn't guarantee this will work; nor does it
forbid it. I've seen enough unexpected derivations from the entire body
of the standard to hold out some hope that that might the case here.)
Failing that, it would be nice to hear, from those intimately involved
in the process, that while not something guaranteed by the standard,
this is within "the spirit of C++" and that it's like to work on any
compiler accepted by the C++ community. Or alternatively that there are
already compilers out there on which it *won't* work.
-- Jerry
---
[ 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 <dtribble@technologist.com>
Date: 1999/01/08 Raw View
Jerry Leichter wrote:
> struct base
> { virtual void f() = 0;
> };
>
> struct p : public base
> { void f() { ... };
> };
>
> struct q : public base
> { void f() { ... };
> };
>
> ...
> p* pp = new p;
> q* qq = static_cast<q*>(static_cast<base*>(p));
>
> q->f(); // Legal?
>
> Can one guarantee, within the C++ standard, that the marked line will
> invoke p::f()?
Nope. More about this later.
The static_cast for qq is illegal; you can't use static_cast to cast
a base pointer to a derived pointer. You could try to use a
dynamic_cast, like
q * qq = dynamic_cast<q*>(dynamic_cast<base*>(p));
but you would probably find that qq is set to null, since the
conversion is not technically legal (i.e., the dynamic_cast could
actually check the validity of the cast at run time and fail).
Even if it could be done, you can't assume that _vtable entries
will match up amongst the derived classes of a base class. In your
example, which only has a single _vtable entry, it might very
well work (assuming you can convert a p* to a q*), but there's
no guarantee it will.
Perhaps instead you could separate out the common portions of
classes p and q and make that an intermediate class, which is
inherited by p and q:
class base { ... f(); ... };
class pq: public base { ... common stuff, e.g., f() ... };
class p: public pq { ... };
class q: public pq { ... };
-- David R. Tribble, dtribble@technologist.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: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/01/08 Raw View
On 07 Jan 99 03:25:55 GMT, Jerry Leichter <leichter@smarts.com> wrote:
>struct base
>{ virtual void f() = 0;
>};
>
>struct p : public base
>{ void f() { ... };
>};
>
>struct q : public base
>{ void f() { ... };
>};
>
> ...
> p* pp = new p;
> q* qq = static_cast<q*>(static_cast<base*>(p));
>
> q->f(); // Legal?
>
>Can one guarantee, within the C++ standard, that the marked line will
>invoke p::f()? (It's certainly the case that, with the usual defini-
>tions of vtable's and such, there's really no way this can break, at
>least absent multiple inheritence. But that's not quite the same
>thing.)
In 5.2.9 item 8 they say that you can covert a Base* to a Derived*.
If the object 'base' of type Base* is really a Derived*, then the
cast is well defined and the result is a pointer to the object as
a Derived*. If the object is not really a Derived*, then the
result of the cast is undefined. Here are the actual last two
sentences:
If the rvalue of type "pointer to cv1 B" points to a B
that is actually a sub-object of an object of type D,
the resulting pointer points to the enclosing object of
type D. Otherwise, the result of the cast is
undefined.
Author: Jerry Leichter <leichter@smarts.com>
Date: 1999/01/12 Raw View
| > struct base
| > { virtual void f() = 0;
| };
| >
| > struct p : public base
| > { void f() { ... };
| > };
| >
| > struct q : public base
| > { void f() { ... };
| > };
| >
| > ...
| > p* pp = new p;
| > q* qq = static_cast<q*>(static_cast<base*>(p));
| >
| > q->f(); // Legal?
| >
>
| > Can one guarantee, within the C++ standard, that the marked line
| > will invoke p::f()?
|
| Nope. More about this later.
|
| The static_cast for qq is illegal; you can't use static_cast to cast
| a base pointer to a derived pointer.
This statement, *as you have made it*, is clearly false: static_cast
can perform the inverse of any standard conversion. Converting a
derived pointer to a base pointer is one of the standard conversions.
It is quite true that this cast violates a constraint (that the pointer
being converted actually *does* point to an object *of the appropriate
type*), but that's a different issue. A C++ compiler must accept the
code. What's at issue is whether the resulting program produces
undefined behavior (which unfortunately I'm pretty well convinced it
does, though *in practice* it's almost certain to work. I'd be *very*
interested in examples of compilers in which it *doesn't* work.)
| Even if it could be done, you can't assume that _vtable entries
| will match up amongst the derived classes of a base class. In your
| example, which only has a single _vtable entry, it might very
| well work (assuming you can convert a p* to a q*), but there's
| no guarantee it will.
Think about what that would imply for an implementation! Suppose that
f's index in the vtables for p and q were different. Then when com-
piling:
base* b = function_returning_base*()
b->f();
the compiler would have to generate code to test, at run time, what b's
dynamic type is, and where f is located in vtable's for objects of that
type. Since the compiler can't even know how many classes might be
derived from base, or what they might look like, we're not even talking
about a simple in-line check - the code would have to deal with derived
classes invented well after the code was compiled.
Possible? Sure - languages like Smalltalk do this all the time, since
they really *can* do fully-dynamic method lookup. Likely in a *C++*
compiler? I doubt it.
The same kinds of arguments apply if we posit that the *vtable pointer*
is at different offsets in any two of base, p, and q: To find the
vtable pointer, the compiled code would first have to find the dynamic
type. So the type had better be at a fixed offset. But it seems
unlikely that a compiler would do that - since *something* has to go at
a fixed offset, why not put the vtable pointer there, and store the type
in the vtable, thus saving space in every object?
| Perhaps instead you could separate out the common portions of
| classes p and q and make that an intermediate class, which is
| inherited by p and q:
|
| class base { ... f(); ... };
|
| class pq: public base { ... common stuff, e.g., f() ... };
|
| class p: public pq { ... };
| class q: public pq { ... };
f() is *not* common - if it were, it would be defined in base!
This entire question arises because C++ combines subtyping with
implementation inheritance. Suppose I have:
class ibasic
{ declarations of abstract functions; }
class iextended : public ibasic
{ declarations of some more abstract functions; }
class basic_impl : public ibasic
{ definitions }
class extended_impl : public <what?>
extended_impl's *interface* should logically derive from iextended
(i.e., iextended is a subclass of ibasic). If there are multiple
implementations of these interfaces, users may wish to write code using
ibasic* and iextended*'s; and they'd like to be able to use an
iextended* where an ibasic* is called for.
A specific example may make this a bit clearer: Take ibasic/iextended
to define a kind of iterator (*not* an STL iterator). ibasic is the
const variant; iextended is the non-const variant, which adds methods
that modify the underlying data. If I have a non-const iterator, I'd
really like to be able to pass it to a function that only needs a const
iterator.
However, if I wish to inherit extended_impl's *implementation* from
basic_impl - to avoid writing the same code in both classes - then
extended_impl has to derive from basic_impl instead. But if it does
that, an extended_impl isn't an iextended! Multiple inheritance helps,
but is painful: Every function in ibasic has to be redefined in
extended_impl to call the basic_impl version, eliminating the ambiguity
(and perhaps ibasic has to be inherited virtually? This adds yet more
overhead to something that, on its face, should be simple and obvious).
Another alternative is to put the common code in yet another class,
using different names for all the methods, then have the _impl's inherit
both the interfaces and this implementation class and provide
"forwarding" from the interface names into the common implementation
class. Again, possible but kind of ugly.
What I've considered doing instead is to have an asBasic() method in
iextended; in extended_impl, it's defined to convert "this" as in my
question to a basic_impl*. Granted, it doesn't conform to the spec.
But has anyone seen (or imagined) a C++ compiler where it won't work.
-- Jerry
[ 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: Jerry Leichter <leichter@smarts.com>
Date: 1999/01/07 Raw View
Consider the following setup:
struct base
{ virtual void f() = 0;
};
struct p : public base
{ void f() { ... };
};
struct q : public base
{ void f() { ... };
};
...
p* pp = new p;
q* qq = static_cast<q*>(static_cast<base*>(p));
q->f(); // Legal?
Can one guarantee, within the C++ standard, that the marked line will
invoke p::f()? (It's certainly the case that, with the usual defini-
tions of vtable's and such, there's really no way this can break, at
least absent multiple inheritence. But that's not quite the same
thing.)
(In case you're wondering why you'd want to do this: This is a simpli-
fication of a construct that appears in some code I've come up with to
implement Coplien's "envelope and letter class" technique and its
associated "virtual constructors" in a way that has no space overhead
and allows the combined envelope/letter to be on the stack. q is the
generic "envelope" class that users see; p is an example of a letter
class, which isn't necessarily directly accessible to users at all - it
could have only private constructors, invoked from q's constructor using
friendship.)
-- Jerry
---
[ 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 ]