I have a set of graph classes that can be composed to create directed graphs for different purposes. For instance, it can be a plain directed graph, or functionality for traversal or nested graphs can be added.
TraversableGraph and TraversableNode should inherit a StatusTrait. However, if the TraversableGraph is also a NestedGraph, only TraversableNode should inherit the StatusTrait. The reason is that NestedGraph inherits from the derived node (which inherits from TraversableNode), and I don't want to run into the diamond problem.
My idea is to make the inheritance of StatusTrait conditional for TraversableGraph: Only inherit if the derived class is not a NestedGraph.
Here is my attempt:
#include <type_traits>
// Status trait
// This should be part of traversable nodes and graphs.
// If a traversable graph is also a nested graph, the status trait should not
// become part of the graph, as it is already part of the traversable node
// inherited by the nested graph.
template<class DerivedClass>
class StatusTrait {
public:
auto Status() const -> int { return 0; }
};
class EmptyTrait {};
// Directed graph
class Node {};
class Graph {};
// Nested graph
template<class BaseClass>
class NestedNode
: public BaseClass
{};
template<class BaseClass, class NodeClass>
class NestedGraph
: public BaseClass
, public NodeClass // Nested graphs are also nodes.
{};
// For checking if a graph is a nested graph compile-time.
template<class BaseClass, class NodeClass>
std::true_type IsNestedGraph(NestedGraph<BaseClass, NodeClass> const *);
std::false_type IsNestedGraph(...);
// Traversable graph
template<class BaseClass>
class TraversableNode
: public BaseClass
, public StatusTrait<TraversableNode<BaseClass>>
{};
template<class BaseClass, class DerivedClass>
class TraversableGraph
: public BaseClass
// If the derived class is a nested graph, don't inherit the status trait.
, public std::conditional_t<decltype(IsNestedGraph(std::declval<typename DerivedClass *>()))::value, EmptyTrait, StatusTrait<DerivedClass>>
{};
int main() {
class MyTraversableNode: public TraversableNode<Node> {};
class MyTraversableGraph: public TraversableGraph<Graph, MyTraversableGraph> {};
MyTraversableNode TN;
MyTraversableGraph TG;
TN.Status();
TG.Status();
class MyNestedTraversableNode: public NestedNode<TraversableNode<Node>> {};
class MyNestedTraversableGraph: public NestedGraph<TraversableGraph<Graph, MyNestedTraversableGraph>, MyNestedTraversableNode> {};
MyNestedTraversableNode NTN;
MyNestedTraversableGraph NTG;
NTN.Status();
NTG.Status(); // Ambiguous access, since the status trait is part of both traversable graph and traversable node.
class MyTraversableNestedNode: public TraversableNode<NestedNode<Node>> {};
class MyTraversableNestedGraph: public TraversableGraph<NestedGraph<Graph, MyTraversableNestedNode>, MyTraversableNestedGraph> {};
MyTraversableNestedNode TNN;
MyTraversableNestedGraph TNG;
TNN.Status();
TNG.Status(); // Ambiguous access, since the status trait is part of both traversable graph and traversable node.
return 0;
}
The code does not compile, but complains that NTG.Status() and TNG.Status() is ambiguous. This indicates that TraversableGraph inherits StatusTrait even when the derived class is a NestedGraph.
If I change the condition to check whether the base class is a NestedGraph instead of checking the derived class, the compiler only complains about NTG.Status() being ambiguous.
std::conditional_t<decltype(IsNestedGraph(std::declval<typename BaseClass *>()))::value, EmptyTrait, StatusTrait<DerivedClass>>`
So my code is able to determine whether the base class is a NestedGraph, but not whether the derived class is one. How can I fix this?
Here is an interactive editor where you can play with the code: https://godbolt.org/z/1h4153E1n