I have two base classes A, and B, and a third class C that (virtually) derives from both of them. Each class exposes its own public shared_ptr type.
In another class I have two vectors where I want to add objects of type A to one vector, objects of type B to another vector, and objects of type C to both. This results in three add methods, one for each of those three classes.
My problems arise when I try to further derive from C:
#include <iostream>
#include <memory>
#include <vector>
class A {
public:
using shared_ptr = std::shared_ptr<A>;
virtual ~A() {};
};
class B {
public:
using shared_ptr = std::shared_ptr<B>;
virtual ~B() {};
};
class C : virtual public A, virtual public B {
public:
using shared_ptr = std::shared_ptr<C>;
virtual ~C() {};
};
class D : virtual public C {
public:
virtual ~D() {};
};
class Test {
protected:
std::vector<A::shared_ptr> vecA;
std::vector<B::shared_ptr> vecB;
public:
void add(const A::shared_ptr& o) {
std::cerr << "in A" << std::endl;
vecA.push_back(o);
}
void add(const B::shared_ptr& o) {
std::cerr << "in B" << std::endl;
vecB.push_back(o);
}
void add(const C::shared_ptr& o) {
std::cerr << "in C" << std::endl;
vecA.push_back(o);
vecB.push_back(o);
}
};
int main()
{
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
auto c = std::make_shared<C>();
auto d = std::make_shared<D>();
Test t;
t.add(a);
t.add(b);
t.add(c);
t.add(d);
}
This doesn't work - the resolution of which version of add to call cannot be determined:
test.cc:62:7: error: call to member function 'add' is ambiguous
t.add(d);
~~^~~
test.cc:34:10: note: candidate function
void add(const A::shared_ptr& o) {
^
test.cc:39:10: note: candidate function
void add(const B::shared_ptr& o) {
^
test.cc:44:10: note: candidate function
void add(const C::shared_ptr& o) {
^
I do have the option of simply passing my C object separately to both Test::add(const A::shared_ptr&) and Test::add(const B::shared_ptr&) because in reality the B version of add has additional parameters that resolve the overload but I would prefer that the caller not have to remember to do this.
Is this ambiguity resolvable? My target environment constrains me to C++14.
The standard derived-to-base conversion sequences take the length of the inheritance chain into account when ranking conversion sequences, a close base would be deemed a better conversion sequence than a one that is further up the inheritance chain. And that in turn affects pointers and references too!
Sadly, since smart pointers are user defined types, they cannot benefit from this behavior. All three overloads are viable via a (valid) user defined conversion. And the "ranks" of the individual bases don't affect the ranking of the overloads.
But that doesn't mean we can't re-introduce the ranking impose by a derived-to-base conversion. We just need to do so via another argument. And by employing tag-dispatch, we can do just that.
We can define a helper utility type:
template<int n> struct rank : rank<n - 1> {};
template<> struct rank<0> {};
For any 0 <= i < j <= k, the conversion sequence of rank<k> -> rank<j> will always be deemed better than rank<k> -> rank<i>. So, if we make your overload set inaccessible, and rank them explicitly:
protected:
void add(const A::shared_ptr& o, rank<0>) { /*...*/ }
void add(const B::shared_ptr& o, rank<0>) { /*...*/ }
void add(const C::shared_ptr& o, rank<1>) { /*...*/ }
We can then expose another overload in the form of a function template:
public:
template<typename T>
void add(const std::shared_ptr<T>& o) {
return add(o, rank<10>{});
}
It mainly just forwards to one of the protected overloads, but it adds another argument. A rank tag. This will affect overload resolution too. Even though all three add overloads are viable, the derived-to-base conversion of rank<10> will affect the choice of best one.
Here it is live.
Related
I am trying to find a way to make something similar to this work:
class A {
public:
int x;
};
class A1 : public A {
public:
int y;
};
class A2 : public A {
public:
std::string s;
};
void printer(A1 A1obj){
std::cout << (A1obj.y+1) << std::endl;
}
void printer(A2 A2obj){
std::cout << A2obj.s << std::endl;
}
void printall(A Aobj){
printer(Aobj);
}
In words: I have some code that works with A objects. many functions in my code take A objects as arguments, making no use of the y or s components. however eventually they will call a couple of functions that will behave differently according to whether the input A was really A1 or A2. I was thinking that I could "overload" these, considering both A1 and A2 are children of A. But the above code says no matching function for call to printer(A&)
One solution is to duplicate printall into printall(A1 A1obj) and printall(A2 A2obj) and use function overloading, but this would mean duplication of a many lines of code in my case, so I'd like to avoid it. Are there alternatives other than just merging A1 and A2 into A and, say, create a std::string label inside it to be used with if-else statements?
Option 1: Virtual functions ("run-time polymorphism")
Whatever makes sense as a conceptual thing to do with/to an A, but the details of what that means will vary by specific class, should be a virtual function of A.
class A {
public:
virtual ~A() = default;
// Disable copying to avoid accidental slicing:
A(const A&) = delete;
A& operator=(const A&) = delete;
virtual void print() const = 0;
int x;
};
class A1 : public A {
public:
void print() const override;
int y;
};
class A2 : public A {
public:
void print() const override;
std::string s;
};
void A1::print() const {
std::cout << y+1 << std::endl;
}
void A2::print() const {
std::cout << s << std::endl;
}
void printall(const A& Aobj) {
Aobj.print();
}
Each derived class A1 and A2 overrides the virtual function declared in A, so calling the function via an A reference or pointer will actually call the definition from the derived class.
Option 2: A template function ("compile-time polymorphism")
// Classes and printer overloads as in question
template <class T>
void printall(const T& Aobj) {
printer(Aobj);
}
Define a template function which can take an object of any type, and generates a function for that type as needed. Here T could really be anything at all, not necessarily a class which inherits A, as long as it can be passed to some printer function.
But if there are other printall functions and that declaration makes it too greedy in overload resolution, you can restrict it to only take types that inherit A.
If you can use C++20 "constraints and concepts":
#include <concepts>
template <class T> requires std::derived_from<T, A>
void printall(const T& Aobj) {
printer(Aobj);
}
Otherwise, you'd need something a little trickier:
// C++11 or later:
#include <type_traits>
template <class T>
typename std::enable_if<std::is_base_of<A, T>::value>::type
printall(const T& Aobj) {
printer(Aobj);
}
This has nothing to do with ambiguous overloading. Even if there was only one printer() function, and one child class, this will never work. If you have a parent class, A, it cannot be implicitly converted to any child class, like A1. You can implicitly convert a child class to a parent class, but not the other way around. This is the only way C++ works.
The correct way is to define an abstract print() method in the A superclass, and then implement it in both A1 and A2 subclasses, to invoke the appropriate printer() function. This is precisely why virtual methods exist. Or, just implement each printer() in the subclass directly, as a print() method.
As per [dcl.init.aggr] I cannot aggregate init a type, if it has (among other things) virtual functions, which includes inheriting from a type with a virtual destructor. However, I'd like to avoid having to write a ton of boilerplate constructors.
MWE:
struct Base {
virtual ~Base() {}
};
struct Derived : Base {
int i;
};
int main() {
Derived d1{42}; // not allowed in this fashion
Derived d2{{},42}; // also not allowed
}
In my setting I have a lot of types like Derived and they are all plain structs with a number of members (not necessarily trivial/pod) except for the fact that they have to inherit from Base.
Is there a way to avoid having to write Derived(int i) : Base(), i(i) {} constructors for all of them?
The one solution I could think of, is to exploit the fact that a struct without the inheritance above will gladly emit a default aggregate initialiser. So I compose that struct together with a templated wrapper type.
template <typename T>
struct BaseWrapper : Base, T {
BaseWrapper(T data) : Node(), T(data) {}
BaseWrapper() = delete;
BaseWrapper(BaseWrapper const&) = default;
BaseWrapper(BaseWrapper&&) = default;
BaseWrapper& operator=(BaseWrapper const&) = default;
BaseWrapper& operator=(BaseWrapper&&) = default;
static T const& cast(Base const& b) {
return static_cast<T const&>(static_cast<BaseWrapper<T> const&>(b));
}
static T& cast(Base& b) {
return static_cast<T&>(static_cast<BaseWrapper<T>&>(b));
}
};
And since I use the Derived types as shared pointers, a small convenience function:
template <typename T, typename... Args>
inline std::shared_ptr<BaseWrapper<T>> make_bw(Args&&... args) {
return std::make_shared<BaseWrapper<T>>(T{std::forward<Args>(args)...});
}
Allows us to create objects without the need of a dedicated constructor inside the object:
struct Derived { // note the missing : Base
int i;
};
auto p = make_bw<Derived>(42);
This is a bit of a cheaty solution, so a proper answer would still be useful.
Suppose I have the class
class A {
protected:
int x,y;
double z,w;
public:
void foo();
void bar();
void baz();
};
defined and used in my code and the code of others. Now, I want to write some library which could very well operate on A's, but it's actually more general, and would be able to operate on:
class B {
protected:
int y;
double z;
public:
void bar();
};
and I do want my library to be general, so I define a B class and that's what its APIs take.
I would like to be able to tell the compiler - not in the definition of A which I no longer control, but elsewhere, probably in the definition of B:
Look, please try to think of B as a superclass of A. Thus, in particular, lay it out in memory so that if I reinterpret an A* as a B*, my code expecting B*s would work. And please then actually accept A* as a B* (and A& as a B& etc.).
In C++ we can do this the other way, i.e. if B is the class we don't control we can perform a "subclass a known class" operation with class A : public B { ... }; and I know C++ doesn't have the opposite mechanism - "superclass a known class A by a new class B". My question is - what's the closest achievable approximation of this mechanism?
Notes:
This is all strictly compile-time, not run-time.
There can be no changes whatsoever to class A. I can only modify the definition of B and code that knows about both A and B. Other people will still use class A, and so will I if I want my code to interact with theirs.
This should preferably be "scalable" to multiple superclasses. So maybe I also have class C { protected: int x; double w; public: void baz(); } which should also behave like a superclass of A.
You can do the following:
class C
{
struct Interface
{
virtual void bar() = 0;
virtual ~Interface(){}
};
template <class T>
struct Interfacer : Interface
{
T t;
Interfacer(T t):t(t){}
void bar() { t.bar(); }
};
std::unique_ptr<Interface> interface;
public:
template <class T>
C(const T & t): interface(new Interfacer<T>(t)){}
void bar() { interface->bar(); }
};
The idea is to use type-erasure (that's the Interface and Interfacer<T> classes) under the covers to allow C to take anything that you can call bar on and then your library will take objects of type C.
I know C++ doesn't have the opposite mechanism - "superclass a known
class"
Oh yes it does:
template <class Superclass>
class Class : public Superclass
{
};
and off you go. All at compile time, needless to say.
If you have a class A that can't be changed and need to slot it into an inheritance structure, then use something on the lines of
template<class Superclass>
class Class : public A, public Superclass
{
};
Note that dynamic_cast will reach A* pointers given Superclass* pointers and vice-versa. Ditto Class* pointers. At this point, you're getting close to Composition, Traits, and Concepts.
Normal templates do this, and the compiler will inform you when you use them incorrectly.
instead of
void BConsumer1(std::vector<B*> bs)
{ std::for_each(bs.begin(), bs.end(), &B::bar); }
void BConsumer2(B& b)
{ b.bar(); }
class BSubclass : public B
{
double xplusz() const { return B::x + B::z; }
}
you write
template<typename Blike>
void BConsumer1(std::vector<Blike*> bs)
{ std::for_each(bs.begin(), bs.end(), &Blike::bar); }
template<typename Blike>
void BConsumer2(Blike& b)
{ b.bar(); }
template<typename Blike>
class BSubclass : public Blike
{
double xplusz() const { return Blike::x + Blike::z; }
}
And you use BConsumer1 & BConsumer2 like
std::vector<A*> as = /* some As */
BConsumer1(as); // deduces to BConsumer1<A>
A a;
BConsumer2(a); // deduces to BConsumer2<A>
std::vector<B*> bs = /* some Bs */
BConsumer1(bs); // deduces to BConsumer1<B>
// etc
And you would have BSubclass<A> and BSubclass<B>, as types that use the B interface to do something.
There is no way to change the behaviour of a class without changing the class. There is indeed no mechanism for adding a parent class after A has already been defined.
I can only modify the definition of B and code that knows about both A and B.
You cannot change A, but you can change the code that uses A. So you could, instead of using A, simply use another class that does inherit from B (let us call it D). I think this is the closest achievable of the desired mechanism.
D can re-use A as a sub-object (possibly as a base) if that is useful.
This should preferably be "scalable" to multiple superclasses.
D can inherit as many super-classes as you need it to.
A demo:
class D : A, public B, public C {
public:
D(const A&);
void foo(){A::foo();}
void bar(){A::bar();}
void baz(){A::baz();}
};
Now D behaves exactly as A would behave if only A had inherited B and C.
Inheriting A publicly would allow getting rid of all the delegation boilerplate:
class D : public A, public B, public C {
public:
D(const A&);
};
However, I think that could have potential to create confusion between code that uses A without knowledge of B and code that uses knows of B (and therefore uses D). The code that uses D can easily deal with A, but not the other way 'round.
Not inheriting A at all but using a member instead would allow you to not copy A to create D, but instead refer to an existing one:
class D : public B, public C {
A& a;
public:
D(const A&);
void foo(){a.foo();}
void bar(){a.bar();}
void baz(){a.baz();}
};
This obviously has potential to mistakes with object lifetimes. That could be solved with shared pointers:
class D : public B, public C {
std::shared_ptr<A> a;
public:
D(const std::shared_ptr<A>&);
void foo(){a->foo();}
void bar(){a->bar();}
void baz(){a->baz();}
};
However, this is presumably only an option if the other code that doesn't know about Bor D also uses shared pointers.
This seems more like static polymorphism rather dynamic. As #ZdeněkJelínek has already mentioned, you could you a template to ensure the proper interface is passed in, all during compile-time.
namespace details_ {
template<class T, class=void>
struct has_bar : std::false_type {};
template<class T>
struct has_bar<T, std::void_t<decltype(std::declval<T>().bar())>> : std::true_type {};
}
template<class T>
constexpr bool has_bar = details_::has_bar<T>::value;
template<class T>
std::enable_if_t<has_bar<T>> use_bar(T *t) { t->bar(); }
template<class T>
std::enable_if_t<!has_bar<T>> use_bar(T *) {
static_assert(false, "Cannot use bar if class does not have a bar member function");
}
This should do what you'd like (i.e. use bar for any class) without having to resort to a vtable lookup and without having the ability to modify classes. This level of indirection should be inlined out with proper optimization flags set. In other words you'll have the runtime efficiency of directly invoking bar.
I am interested if it is safe, to DOWNCAST (thanks Mike) an instance of a base class to a derived class under certain conditions. I think a sample is the most easy way to explain:
struct BaseA
{
void foo() const {}
double bar_member;
// no virtuals here
};
struct DerivedA : public BaseA
{
double bar(double z) {bar_member = z;return bar_member;}
// DerivedA does not add ANY member variables to BaseA.
// It also does not introduce ANY virtual functions.
};
struct BaseB
{
BaseA baseA;
};
// add extra functionality to B, to do this,
// i also need more functionality on baseA.
struct DerivedB : public BaseB
{
// is this "safe"? since BaseA and DerivedA
// should have the same memory layout?!?
DerivedA& getA() {return *static_cast<DerivedA*>(&baseA);}
double foo(double z) {return getA().bar(z);}
};
#include <iostream>
int main(int argc, char** argv)
{
DerivedB b;
// compiles and prints expected result
std::cout << b.foo(argc) << std::endl;
}
In my case, the classes BaseA and BaseB implement some kind of view concept. However, they also hold all the data members required to add further functionality in the derived classes. I know that I could implement the view to hold only a reference to the class providing the functionality. However, that would comes with some drawbacks:
I need to rewrite the whole interface for the view classes.
In my case, the Derived classes possesses an extra template argument (a callback type), which I want to have erased in the view. Hence, the view must not hold a direct reference to the classes providing functionality.
I tested my code, it works, however, I don't really trust the approach. And yes, I know I could achieve some of this with virtuals etc. but it is really performance critical...
Any ideas, hints, are welcome
Martin
for the interested people:
i changed my design the following way:
struct DerivedB : public BaseB
{
// encapsule the required extended functionality of BaseA member
struct OperateOnBaseA
{
OperateOnBaseA(BaseA& a);
double dosomething(double);
};
OperateOnBaseA a_extension;
DerivedB() :a_extension(baseA) {}
double foo(double z) {return a_extension.dosomething();}
};
As to the technical side: It is of course forbidden by the 2011 standard, 5.2.9.11, static cast. Let B be a base of D:
If the prvalue of type “pointer to cv1 B” points to a B that is actually a subobject 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.
On the other hand I'd be surprised if somebody could find an implementation which doesn't just do it, because of the obvious implementations of classes, methods and static casts.
Your existing code has undefined behaviour, as stated in the other answers. You can avoid that, if you don't mind some truly horrible code, by destroying the object at baseA and creating a DerivedA at the same location, so the downcast is valid:
#include <new>
struct DerivedB : public BaseB
{
DerivedB()
{
static_assert( sizeof(BaseA) == sizeof(DerivedA), "same size" );
baseA.~BaseA();
::new(&baseA) DerivedA();
}
~DerivedB()
{
getA().~DerivedA();
::new(&baseA) BaseA();
}
DerivedA& getA() {return *static_cast<DerivedA*>(&baseA);}
double foo(double z) {return getA().bar(z);}
};
The destructor restores an object of the original type, so that when the BaseB destructor destroys its baseA member it runs the destructor of the correct type on the object.
But I would avoid doing this and redesign your classes to solve it another way.
I don't find your approad clean enough for what you're trying to do. Assuming there's a "data source type" SourceA and a "data view type" ViewB, I would go more like this:
#include <iostream>
using namespace std;
template<typename T>
class SourceA_base
{
protected:
T data;
public:
using value_type = T;
SourceA_base(T&& a) : data(std::move(a)) { }
SourceA_base(T const& a) : data() { }
void foo() const {}
};
template<typename T>
class SourceA : public SourceA_base<T>
{
using B = SourceA_base<T>;
public:
using B::B;
T bar(T z) { return B::data = z; }
};
template<typename U>
class ViewB_base
{
protected:
U&& source;
public:
using value_type = typename std::remove_reference<U>::type::value_type;
ViewB_base(U&& a) : source(std::forward<U>(a)) { }
};
template<typename U>
class ViewB : public ViewB_base<U>
{
using B = ViewB_base<U>;
using T = typename B::value_type;
public:
using B::B;
T foo(T z) { return B::source.bar(z); }
};
int main ()
{
using S = SourceA<double>;
S a{3.14};
ViewB<S&> b{a};
std::cout << b.foo(6.28) << std::endl; // compiles and prints expected result
std::cout << ViewB<S>{S{2}}.foo(4) << std::endl; // still works
}
That is, all (source/view) types are templated, views contain references, and there are no downcasts. On your reservations for the use of references:
Re-writing the whole interface: No need now, thanks to templates.
Erasing callback types: First, type erasure and performance critical applications are not always good friends. Second, you'd better have the callback erase its own underlying type(s), not the view erase the type of the callback. Each class should do its own job. Or, don't erase types and make them template parameters.
I used rvalue-references so that the whole thing works for temporaries as well, as shown in my second example. Maybe constructors are not always complete/correct here. e.g. for const references; I reality would have fully templated constructors (accepting universal references), but to make this cooperate with one-argument implicitly defined copy/move constructors is a bit trickier (needs type traits and enable_if) and I only wanted to hightlight the idea here.
You may also consider using tuples to hold data, taking advantage of their empty base optimization.
As for your original question, this downcast is something I would never do; for the technical side, see Peter Schneider's answer.
Consider classic virtual inheritance diamond hierarchy. I wonder to know what is the right implementation of copy and swap idiom in such hierarchy.
The example is a little artificial - and it is not very smart - as it would play good with default copy semantic for A,B,D classes. But just to illustrate the problem - please forget about the example weaknesses and provide the solution.
So I have class D derived from 2 base classes (B<1>,B<2>) - each of B classes inherits virtually from A class. Each class has non trivial copy semantics with using of copy and swap idiom. The most derived D class has problem with using this idiom. When it calls B<1> and B<2> swap methods - it swaps virtual base class members twice - so A subobject remains unchanged!!!
A:
class A {
public:
A(const char* s) : s(s) {}
A(const A& o) : s(o.s) {}
A& operator = (A o)
{
swap(o);
return *this;
}
virtual ~A() {}
void swap(A& o)
{
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const A& a) { return os << a.s; }
private:
S s;
};
B
template <int N>
class B : public virtual A {
public:
B(const char* sA, const char* s) : A(sA), s(s) {}
B(const B& o) : A(o), s(o.s) {}
B& operator = (B o)
{
swap(o);
return *this;
}
virtual ~B() {}
void swap(B& o)
{
A::swap(o);
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const B& b)
{ return os << (const A&)b << ',' << b.s; }
private:
S s;
};
D:
class D : public B<1>, public B<2> {
public:
D(const char* sA, const char* sB1, const char* sB2, const char* s)
: A(sA), B<1>(sA, sB1), B<2>(sA, sB2), s(s)
{}
D(const D& o) : A(o), B<1>(o), B<2>(o), s(o.s) {}
D& operator = (D o)
{
swap(o);
return *this;
}
virtual ~D() {}
void swap(D& o)
{
B<1>::swap(o); // calls A::swap(o); A::s changed to o.s
B<2>::swap(o); // calls A::swap(o); A::s returned to original value...
s.swap(o.s);
}
friend std::ostream& operator << (std::ostream& os, const D& d)
{
// prints A::s twice...
return os
<< (const B<1>&)d << ','
<< (const B<2>&)d << ','
<< d.s;
}
private:
S s;
};
S is just a class storing string.
When doing copy you will see A::s remains unchanged:
int main() {
D x("ax", "b1x", "b2x", "x");
D y("ay", "b1y", "b2y", "y");
std::cout << x << "\n" << y << "\n";
x = y;
std::cout << x << "\n" << y << "\n";
}
And the result is:
ax,b1x,ax,b2x,x
ay,b1y,ay,b2y,y
ax,b1y,ax,b2y,y
ay,b1y,ay,b2y,y
Probably adding B<N>::swapOnlyMewould resolve the problem:
void B<N>::swapOnlyMe(B<N>& b) { std::swap(s, b.s); }
void D::swap(D& d) { A::swap(d); B<1>::swapOnlyMe((B<1>&)d); B<2>::swapOnlyMe((B<2>&)d); ... }
But what when B inherits privately from A?
Here's a philosophical rant:
I don't think virtual inheritance can or should be private. The entire point of a virtual base is that the most derived class owns the virtual base, and not the intermediate classes. Thus no intermediate class should be permitted to "hog" the virtual base.
Let me repeat the point: The most derived class owns the virtual base. This is evident in constructor initializers:
D::D() : A(), B(), C() { }
// ^^^^
// D calls the virtual base constructor!
In the same sense, all other operations in D should be immediately responsible for A. Thus we are naturally led to writing the derived swap function like this:
void D::swap(D & rhs)
{
A::swap(rhs); // D calls this directly!
B::swap(rhs);
C::swap(rhs);
// swap members
}
Putting all this together, we're left with only one possible conclusion: You have to write the swap functions of the intermediate classes without swapping the base:
void B::swap(B & rhs)
{
// swap members only!
}
void C::swap(C & rhs)
{
// swap members only!
}
Now you ask, "what if someone else wants to derive from D? Now we see the reason for Scott Meyer's advice to always make non-leaf classes abstract: Following that advice, you only implement the final swap function which calls the virtual base swap in the concrete, leaf classes.
Update: Here's something only tangentially related: Virtual swapping. We continue to assume that all non-leaf classes are abstract. First off, we put the following "virtual swap function" into every base class (virtual or not):
struct A
{
virtual void vswap(A &) = 0;
// ...
};
Usage of this function is of course reserved only to identical types. This is safeguarded by an implicit exception:
struct D : /* inherit */
{
virtual void vswap(A & rhs) { swap(dynamic_cast<D &>(rhs)); }
// rest as before
};
The overall utility of this is limited, but it does allow us swap to objects polymorphically if we happen to know that they're the same:
std::unique_ptr<A> p1 = make_unique<D>(), p2 = make_unique<D>();
p1->vswap(*p2);
Virtual base generally means that most derived class of object is in control of it.
First solution: Reorganize your classes to be more fit for polymorphism. Make copy construction protected. Remove assignment and swap(). Add virtual clone(). Idea is that the classes should be treated as polymorphic. So they should be used with pointer or smart pointer. Swapped or assigned should be the pointer values, not the object values. In such context swap and assignment only confuse.
Second solution: Make B and C abstract and their pointers not to manage object lifetime. Destructors of B and C should be protected and non-virtual. B and C will not therefore be most derived classes of object. Make B::swap() and C::swap() protected and not swap the A subobject, may rename or add comment that it is business of inherited classes now. That removes lot of object slicing possibilities. Make D::swap() to swap A subobject. You get one swap of A.
Third solution: Make D::swap() to swap A subobject. That way the A subobject will be swapped 3 times and lands in correct place. Inefficient? The whole construct is probably bad idea anyway. I am for example not sure how well the virtual destructors and swaps cooperate here and lot of ways to slice objects are public here. It all seems similar to attempt of making virtual assignment operators that is bad idea in C++.
If something inherits from D on its order then it should make sure by swapping or not swapping A subobject that the count of swapping A is odd. It becomes to control so should take over and fix.
The private virtual idiom is one of ways to make a class final in C++. Nothing should be able to inherit from it. Interesting that you asked. If you ever use it, be sure to comment, it confuses most of the readers of code.