* Jeff Greenberg:[color=blue]
> Alf,
>
> Thanks for the reply. I looked at your first suggestion:
>
> In article <4173098a.407274312@news.individual.net>,
>
alfps@start.no (Alf P. Steinbach) wrote:
>[color=green][color=darkred]
> >> >
> > > CONS: The member function foo() can't be implemented in the base class
> > > because it will cause object slicing when used in the derived classes.[/color]
> >
> > Well that isn't quite correct.
> >
> > One way is to use a protected template function:
> >
> > class Base
> > {
> > protected:
> > template< class Result >
> > Result foo_() const
> > {
> > return Result();
> > }
> > public:
> > virtual ~Base() {}
> > virtual Base foo() const { return foo_<Base>(); }
> > };
> >
> > class Derived: public Base
> > {
> > public:
> > virtual Derived foo() const { return foo_<Derived>(); }
> > };
> >[/color]
>
> Unfortunately, this does not compile (at least on any compiler I have)
> because
>[color=green]
> > virtual Derived foo() const { return foo_<Derived>(); }[/color]
>
> is an illegal override of
>[color=green]
> > virtual Base foo() const { return foo_<Base>(); }[/color]
>
> since Base and Derived are not covariant. Of course it would work if we
> were returning Base& and Derived& but that leads back to my original
> problem. Did you have something else in mind?[/color]
No it was just sloppy thinking in a hasty reply. Full points on creativity,
zero on attention to detail. The following slight adjustment compiles (;-)):
#include <iostream>
#include <memory>
class Base
{
protected:
template< class Result >
Result foo_() const
{
return Result();
}
public:
virtual ~Base() {}
virtual Base foo( Base* = 0 ) const { return foo_<Base>(); }
};
class Derived: public Base
{
public:
virtual Derived foo( Derived* = 0 ) const
{ return foo_<Derived>(); }
virtual Base foo( Base* ) const
{ return foo( static_cast<Derived*>(0) ); }
};
int main()
{
Base().foo();
Derived().foo();
}
The implementation of Derived::Foo(Base*) in terms of Derived::foo(Derived*)
is to avoid O(n^2) overrides in an n-classes inheritance chain. It does
involve a lot of object creation and destruction, though, modulo optimization.
However as I understood it that was OK.
[color=blue][color=green]
> > Another way is to use dynamic allocation via a protected virtual
> > factory function. To avoid memory leaks the foo() result should
> > then be a smart-pointer, the handle/body-pattern. Smart-pointers
> > can work with covariance when they're not reseatable. From the
> > rest of what you write I presume you're aware of why it's not a
> > good idea with reseatable smart-pointers in this context.[/color]
>
> You give me more credit than I deserve. I'm not sure how
> "non-reseatable" (is that the same as "non-resetable"?) smart pointers
> can help. To be covariant, the return type must be a pointer to a
> derived type of the base class return. The smart pointer libs that I'm
> aware of all work through templates. For example:
>
> boost::shared_ptr<Base> has no inheritance relationship with
> boost:shared_ptr<Derived>. So how does this help?[/color]
Uh, hm, well... Checking it out it's no good to return a temporary as a
reference to const because function return value is one of the exceptions to
the general rule that the temporary should persist. Dang!
But hey, now it seems clear why Andrei was asking about lifetime of
temporaries created for arguments.
Temporary storage is in general not good enough for a reference result because
that result might be passed back up the function call chain, or bound to
a local reference, or whatever. That's one of those situations where the
language isn't exactly helpful. The FAQ's solution to such dilemmas is to
use a comment with a stern warning about what the client should not ever do.
In the following code I haven't addressed the issue of factoring out the
common foo() functionality, because you don't specify anything about that.
Nor does it ensure that all access is via smart-pointers, because that
depends on the concrete design, and would just clutter things here, and
most of all, this is untested code -- it compiles, but that's it... ;-)
#include <memory>
class Base
{
protected:
class TempPtrStorage;
public:
class Ptr
{
friend class TempPtrStorage;
private:
Ptr( Ptr const& ); // Nope.
Ptr& operator=( Ptr const& ); // Nope.
protected:
std::auto_ptr<Base> myP;
Ptr( Base* p ): myP( p ) {}
public:
Base const* operator->() const { return myP.get(); }
};
protected:
class TempPtrStorage
{
friend class Base;
private:
mutable std::auto_ptr<Ptr> myStorage; // Could/should be optimized.
protected:
template< class PtrType, class Contained >
PtrType& smartPointer( Contained* pContained ) const
{
myStorage.reset( new PtrType( pContained ) );
return *static_cast<PtrType*>( myStorage.get() );
}
};
template< class PtrType, class Contained >
PtrType& smartPointer( TempPtrStorage const& storage, Contained*
pContained ) const
{
return storage.smartPointer<PtrType>( pContained );
}
public:
// Bind the result to a ref, or take its address, then you're fired.
virtual Base::Ptr const& foo(
TempPtrStorage const& stg = TempPtrStorage()
) const
{
return smartPointer<Base::Ptr>( stg, new Base() );
}
};
class Derived: public Base
{
public:
class Ptr: public Base::Ptr
{
friend class TempPtrStorage;
protected:
Ptr( Derived* p ): Base::Ptr( p ) {}
public:
Derived const* operator->() const
{
return static_cast<Derived*>( myP.get() );
}
};
// Bind the result to a ref, or take its address, then you're fired.
virtual Derived::Ptr const& foo(
TempPtrStorage const& stg = TempPtrStorage()
) const
{
return smartPointer<Derived::Ptr>( stg, new Derived() );
}
};
int main()
{
Derived d;
Base& bRef = d;
bRef.foo(); // Calls Derived::foo.
}
[color=blue][color=green]
> > However,
> > an easier solution is to note that anybody wanting a Derived-specific
> > result know they're dealing with Derived and so can call a Derived-
> > specific version of foo; this even works with std::auto_ptr:
> >
> > class Base
> > {
> > private:
> > virtual Base* rawFoo() const { return new Base; }
> > public:
> > typedef std::auto_ptr<Base> AutoPtr;
> >
> > virtual ~Base() {}
> > AutoPtr foo() const { return AutoPtr( rawFoo() ); }
> > };
> >
> > class Derived: public Base
> > {
> > private:
> > virtual Derived* rawFoo() const { return new Derived; }
> > public:
> > typedef std::auto_ptr<Derived> AutoPtr;
> >
> > AutoPtr derivedFoo() const{ return AutoPtr( rawFoo() ); }
> > };
> >
> > int main()
> > {
> > Derived().foo(); // calls Derived::rawFoo, Base::AutoPtr
> > Derived().derivedFoo(); // ditto, but now Derived::AutoPtr
> > }[/color]
>
> This would work for many applications but in this application it leads
> to unacceptable syntax in the calling program. Operations like "foo()"
> represent generic mathematical transformations that are logically
> identical so it would not make much sense to write:
>
> Base().sqrt();
>
> and then
>
> Derived().derivedSqrt();
>
> when both operations imply the same mathematical transformation.
> Requiring such syntax would lead to a user revolt :)[/color]
Well I think you could just use the dummy argument trick exemplified
at top, to call both functions 'sqrt' (differentiating on argument type
instead of name, where the client code never uses that argument).
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?