I want to add swap functionality to two existing C++ classes. One class inherits from the other. I want each classes' instances to only be swappable with instances of the same class. To make it semi-concrete, say I have classes Foo and Bar. Bar inherits from Foo. I define Foo::swap(Foo&) and Bar::swap(Bar&). Bar::swap delegates to Foo::swap. I want Foo::swap to only work on Foo instances and Bar::swap to only work on Bar instances: I can't figure out how to enforce this requirement.
Here's a sample of what's giving me trouble:
#include <algorithm>
#include <iostream>
struct Foo {
int x;
Foo(int x) : x(x) {};
virtual void swap(Foo &other) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
std::swap(this->x, other.x);
};
};
struct Bar : public Foo {
int y;
Bar(int x, int y) : Foo(x), y(y) {};
virtual void swap(Bar &other) {
std::cout << __PRETTY_FUNCTION__ << " ";
Foo::swap(other);
std::swap(this->y, other.y);
};
};
void display(Foo &f1, Foo &f2, Bar &b34, Bar &b56)
{
using namespace std;
cout << "f1: " << f1.x << endl;
cout << "f2: " << f2.x << endl;
cout << "b34: " << b34.x << " " << b34.y << endl;
cout << "b56: " << b56.x << " " << b56.y << endl;
}
int main(int argc, char **argv)
{
{
Foo f1(1), f2(2);
Bar b34(3,4), b56(5,6);
std::cout << std::endl << "Initial values: " << std::endl;
display(f1,f2,b34,b56);
}
{
Foo f1(1), f2(2);
Bar b34(3,4), b56(5,6);
std::cout << std::endl << "After Homogeneous Swap: " << std::endl;
f1.swap(f2); // Desired
b34.swap(b56); // Desired
display(f1,f2,b34,b56);
}
{
Foo f1(1), f2(2);
Bar b34(3,4), b56(5,6);
std::cout << std::endl << "After Heterogeneous Member Swap: " << std::endl;
// b56.swap(f2); // Doesn't compile, excellent
f1.swap(b34); // Want this to not compile, but unsure how
display(f1,f2,b34,b56);
}
return 0;
}
Here's the output:
Initial values:
f1: 1
f2: 2
b34: 3 4
b56: 5 6
After Homogeneous Swap:
virtual void Foo::swap(Foo&)
virtual void Bar::swap(Bar&) virtual void Foo::swap(Foo&)
f1: 2
f2: 1
b34: 5 6
b56: 3 4
After Heterogeneous Member Swap:
virtual void Foo::swap(Foo&)
f1: 3
f2: 2
b34: 1 4
b56: 5 6
You can see in the final output group where f1.swap(b34) "sliced" b34 in a potentially nasty way. I'd like the guilty line to either not compile or blow up at runtime. Because of the inheritance involved, I think I run into the same problem if I use a nonmember or friend swap implementation.
The code is available at codepad if that helps.
This use case arises because I want to add swap to boost::multi_array and boost::multi_array_ref. multi_array inherits from multi_array_ref. It only makes sense to swap multi_arrays with multi_arrays and multi_array_refs with multi_array_refs.
Swap, like assignment and comparison work well with value types and don't work well with bases of class hierarchies.
I've always found it easiest to follow the Scott Meyer's Effective C++ recommendation of not deriving from concrete classes and making only leaf classes concrete. You can then safely implement swap, operator==, etc. as non-virtual functions for leaf nodes only.
While it's possible to have a virtual swap functions the whole point of having virtual base classes is to have dynamic behaviour at runtime so I think you're on to a loser trying to get all incorrect possibilities to fail at compile time.
If you want to go the virtual swap route, then one possible approach is to do something like this.
class Base
{
public:
virtual void Swap(Base& other) = 0;
};
class ConcreteDerived
{
virtual void Swap(Base& other)
{
// might throw bad_cast, in this case desirable
ConcreteDerived& cother = dynamic_cast<ConcreteDerived&>(other);bad_cast
PrivateSwap(cother);
}
void PrivateSwap(ConcreteDerived& other)
{
// swap implementation
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With