Conditional inheritance based on derived type

Viewed 60

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

0 Answers
Related