Imagine that I have two similar structures A and B. Both A and B have a pointer to A, but A has an additional pointer to either A or B.
I thought of something like this with a Base and a Derived class
template <bool T> struct Derived;
struct Base { Derived<0> *p1; };
template <> struct Derived<0> : public Base { Base *p2; };
template <> struct Derived<1> : public Base {};
where A is Derived<0>
and B is Derived <1>
.
The problem here is that when accessing a class through p2
, the compiler does not know which of the Derived classes it is, and something like this gives an error.
Derived<0> x, y, z;
x.p2 = &y;
y.p2 = &z;
x.p2->p2; // Error
Do any of you know any magical workaround, preferably using only compile-time features?
I also need to know which type of Derived am I using, so that I know if I can use p2 or not.
If it helps, you can visualise things as a double-linked list, where Derived<0>
is a normal node, Derived<1>
is the end node, p1
is the prev pointer and p2
is the next pointer.
Edit: It does not need to use a Base-and-Derived-class-type structure, it can be anything.
A possible solution is based on double dispatching, the same idea that is behind the most known visitor pattern.
It follows a minimal, working example:
#include<iostream>
template<int>
struct Derived;
struct Visitor {
template<int N>
void visit(Derived<N> &);
};
struct Base {
Derived<0> *p1;
virtual void accept(Visitor &) = 0;
};
template<>
struct Derived<0>: public Base {
void accept(Visitor &) override;
Base *p2;
};
template<>
struct Derived<1>: public Base {
void accept(Visitor &) override;
};
template<>
void Visitor::visit(Derived<0> &d) {
std::cout << "Derived<0>" << std::endl;
d.p2->accept(*this);
}
template<>
void Visitor::visit(Derived<1> &) {
std::cout << "Derived<1>" << std::endl;
}
void Derived<0>::accept(Visitor &v) {
v.visit(*this);
}
void Derived<1>::accept(Visitor &v) {
v.visit(*this);
}
int main() {
Visitor v;
Derived<0> x, y;
Derived<1> z;
x.p2 = &y;
y.p2 = &z;
x.p2->accept(v);
}
See it up and running on wandbox.
If you can use C++17 and thus std::variant
, things are far more simple:
#include<iostream>
#include<variant>
template<int>
struct Derived;
struct Base {
Derived<0> *p1;
};
template<>
struct Derived<0>: public Base {
std::variant<Derived<0> *, Derived<1> *> p2;
};
template<>
struct Derived<1>: public Base {};
struct Visitor {
void operator()(Derived<0> *d) {
std::cout << "Derived<0>" <<std::endl;
std::visit(*this, d->p2);
}
void operator()(Derived<1> *) {
std::cout << "Derived<1>" <<std::endl;
}
};
int main() {
Visitor v;
Derived<0> x, y;
Derived<1> z;
x.p2 = &y;
y.p2 = &z;
std::visit(v, x.p2);
}
See it up and running on wandbox.