How to make type alias invisible to derived classes?

73 Views Asked by At

I have a set of base graph classes that use CRTP to work with the derived graph classes. I have a template class GraphTypes that take the derived graph classes as template parameters, and then the base graph classes, like Edge and Node, take GraphTypes as template parameter.

Inside the base graph classes, I use type aliases (using) to simplify using the derived classes. I would like these type aliases to only be visible inside the base graph classes, and not in the derived graph classes. Is this possible?

See the code below for the setup, and an issue where the type alias in the base graph class is being used in the derived class.

namespace DirectedGraph 
{
    template<class NodeT, class EdgeT>
    class GraphTypes 
    {
    public:
        using NodeType = NodeT;
        using EdgeType = EdgeT;
    };

    template<class GraphTypes>
    class Edge 
    {
    private:
        using NodeType = typename GraphTypes::NodeType;
        using EdgeType = typename GraphTypes::EdgeType;
    public:
        auto This() -> EdgeType&;
    };
}

namespace MyGraph
{
    class Node;
    class Edge;
    using GraphTypes = DirectedGraph::GraphTypes<Node, Edge>;

    enum class EdgeType { A, B, C };

    class Edge : public DirectedGraph::Edge<GraphTypes>
    {
        Edge(EdgeType Type); // `EdgeType` is interpreted as the type alias `DirectedGraph::Edge::EdgeType` instead of the enum `MyGraph::EdgeType`.
    };
}
2

There are 2 best solutions below

0
Jan Schultke On BEST ANSWER

I am assuming that you don't want to rename EdgeType in Edge. If you were willing to do that, you could just use different names for different aliases and not run into this issue.

To solve this issue without changing any names, you can shadow DirectedGraph::Edge<T>::EdgeType by declaring another alias inside of MyGraph::Edge:

class Edge : public DirectedGraph::Edge<GraphTypes> {
    // possibly make this private
    using EdgeType = MyGraph::EdgeType;

    // we could also use MyGraph::EdgeType directly here, but that would be uglier
    Edge(EdgeType Type);
};

The MyGraph::Edge::EdgeType alias will shadow any declarations in the scope of the base class and in the surrounding scope, so you can disambiguate like this.

0
JeJo On

You can always qualify the full namespace

namespace MyGraph {
    class Node;
    class Edge;
    using GraphTypes = DirectedGraph::GraphTypes<Node, Edge>;

    enum class EdgeType { A, B, C };

    class Edge : public DirectedGraph::Edge<GraphTypes> {
        Edge(::MyGraph::EdgeType Type);
        //   ^^^^^^^^^^^^^^^^^^
        // or
        // Edge(MyGraph::EdgeType Type);
    };
}

Or use different names for types/ type aliases for avoiding confusion.


That being said, if you insist on keeping the names as it is, and do not want to use them from the base class, provide an indirection to those alias types, by a class/struct.

namespace DirectedGraph 
{
    // ....

    template<class GraphTypes>
    class Edge {
    private:
    public:
        struct Internal { // moved under the Internal struct!
            using NodeType = typename GraphTypes::NodeType;
            using EdgeType = typename GraphTypes::EdgeType;
        };

        auto This() -> typename Internal::EdgeType&;
    };
}

namespace MyGraph {
    // ....
    enum class EdgeType { A, B, C };

    class Edge : public DirectedGraph::Edge<GraphTypes> {
        Edge(EdgeType Type); //Now you can!
    };
}

Demo