Multiple dispatch problem with swap()

M

Marcel Müller

I have a base class that provides a swap() method.
In a derived class I have some caching. Swapping the Base slice makes
sense, but it invalidates the cache. No problem if *this is the derived
class, I could simply override swap. But what if the derived class is
the argument to swap?

class Base
{
virtual void swap(Base& right);
};

class Derived
{
int SomeCache;
virtual void swap(Base& right);
}

Base b();
Derived d();
b.swap(d);

Then Base::swap is called, which obviously knows nothing about Derived.

Is there a way to come around that?


Marcel
 
J

Juha Nieminen

Marcel Müller said:
class Base
{
virtual void swap(Base& right);
};

class Derived
{
int SomeCache;
virtual void swap(Base& right);
}

Base b();
Derived d();
b.swap(d);

Then Base::swap is called, which obviously knows nothing about Derived.

Is there a way to come around that?

Maybe something like this:

class Base
{
public:
virtual void swap(Base& right)
{
if(!right.peformSwap(*this))
{
// perform swap here
}
}

protected:
virtual bool performSwap(Base&) { return false; }
};

class Derived: public Base
{
public:
virtual void swap(Base& right)
{
// perform specialized swap here
}

protected:
virtual bool performSwap(Base& right)
{
swap(right);
return true;
}
};
 
S

SG

I have a base class that provides a swap() method.
In a derived class I have some caching. Swapping the Base slice makes
sense, but it invalidates the cache. No problem if *this is the derived
class, I could simply override swap. But what if the derived class is
the argument to swap?

class Base
{
   virtual void swap(Base& right);
};

class Derived
{
   int SomeCache;
   virtual void swap(Base& right);
}

Base b();
Derived d();
b.swap(d);

Then Base::swap is called, which obviously knows nothing about Derived.

Is there a way to come around that?

I think the design is questionable. If you want to protect your
objects from outsiders messing with the subobjects (like the base
class subobject of a Derived object), you generally make this
subobjekt private. Obviously, this rules out public inheritance. Also,
myDerivedObj.swap(myBaseObj); seems very wrong. What are you actually
trying to achieve here?

Cheers!
SG
 
M

Marcel Müller

I think the design is questionable. If you want to protect your
objects from outsiders messing with the subobjects (like the base
class subobject of a Derived object), you generally make this
subobjekt private. Obviously, this rules out public inheritance. Also,
myDerivedObj.swap(myBaseObj); seems very wrong. What are you actually
trying to achieve here?

Base and Derived are relative paths. Derived uses some caching for the
objects in the current path to speed up some operations. This cache is
not useful in all cases.

A pattern is to manipulate local copies of a path and then swap. The
local copies as well as the long lived instances might be of type Base
or Derived, depending on whether the cache is helpful or not. The public
interfaces of the workers only expose Base.
In general swap of Derived invalidates parts of the cache. But if both
instances are of type Derived, the cache should be swapped too instead
of invalidated. This requires double dispatch.


Marcel
 
M

Marcel Müller

Maybe something like this:

class Base
{
public:
virtual void swap(Base& right)
{
if(!right.peformSwap(*this))
{
// perform swap here
}
}

protected:
virtual bool performSwap(Base&) { return false; }
};
[...]

Thanks, I did something like that now. I does not look too pretty, but
that seems to be common to all multiple dispatch solutions in C++. And
your code seems to be one of the simplest solutions for double dispatch.
I had to modify it slightly, because the swap code of the base class has
always to be executed. Only the post processing varies depending on the
types.


Marcel
 
P

Pavel

Marcel said:
Base and Derived are relative paths. Derived uses some caching for the objects
in the current path to speed up some operations. This cache is not useful in all
cases.

A pattern is to manipulate local copies of a path and then swap. The local
copies as well as the long lived instances might be of type Base or Derived,
depending on whether the cache is helpful or not. The public interfaces of the
workers only expose Base.
In general swap of Derived invalidates parts of the cache. But if both instances
are of type Derived, the cache should be swapped too instead of invalidated.
This requires double dispatch.


Marcel

Just 2c:

You might consider calling you method differently from swap() (something like
swapInternals() or swapGuts() for brevity). swap() is an idiom which IMHO
assumes that the left part before the swap and the right part after the swap are
fully fungible.

In your case, when Derived d is swapped with Base b, `d' type does not become
Base and vice versa so this perceived invariant of the operation breaks.

HTH
-Pavel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,225
Members
46,815
Latest member
treekmostly22

Latest Threads

Top