【问题标题】:Nested class specialization in terms of outer class template arguments根据外部类模板参数的嵌套类特化
【发布时间】:2015-01-28 13:30:25
【问题描述】:

我正在编写一个 Graph 类,我想为同一个类提供多种类型的图。更具体地说,我想考虑有向图或无向图,以及顶点(和边)具有值的可能性。

现在,我的班级看起来像这样。

template<typename VertexValue = void, typename EdgeValue = void, bool directed = false>
class Graph {
public:
    struct Vertex;
    using Edge = Vertex::Edge;

    Graph() = default;
    //void addVertex(const VertexValue& vertex);

private:
    std::vector<Vertex> m_vertices;
};

为方便起见,我将跳过与边缘实现相关的任何内容。

我现在要做的是“专门化” Vertex 类以提供(或不提供)一个值,以及取决于图形是否定向的不同方法。我希望能够执行以下操作(注意:Vertex 方法仅用于说明目的,Vertex 的有向和无向版本具有不同的方法)。

template<typename VertexValue = void, bool directed = false>
class Graph {
public:
    struct Vertex;

private:
    std::vector<Vertex> m_vertices;
};

// Directed, with value
template<typename VertexValue, bool directed>
struct Graph<VertexValue, directed>::Vertex {
    using ValueType = VertexValue;

    EdgeIterator inEdges();
    EdgeIterator outEdges();

    ValueType value;
};

// Directed, without value
template<bool directed>
struct Graph<void, directed>::Vertex {
    using ValueType = void;

    EdgeIterator inEdges();
    EdgeIterator outEdges();
};

// Undirected, with value
template<typename VertexValue>
struct Graph<VertexValue, false>::Vertex {
    using ValueType = VertexValue;

    EdgeIterator edges();

    ValueType value;
};

// Undirected, without value
template<>
struct Graph<void, false>::Vertex {
    using ValueType = void;

    EdgeIterator edges();
};

不幸的是,我必须专门化整个 Graph 类才能做到这一点。所以我尝试了其他方法:将 Vertex 类作为模板,并转发外部模板参数。

template<typename VertexValue = void, bool directed = false>
class Graph {
public:
    template<typename Value, bool, typename dummy = void>
    struct Vertex;

private:
    std::vector<Vertex<VertexValue, directed>> m_vertices;
};

// Directed, with value
template<typename VertexValue, bool directed>
template<typename Value, bool, typename dummy>
struct Graph<VertexValue, directed>::Vertex {
    using ValueType = Value;

    EdgeIterator inEdges();
    EdgeIterator outEdges();

    ValueType value;
};

// Directed, without value
template<typename VertexValue, bool directed>
template<bool directed_, typename dummy>
struct Graph<VertexValue, directed>::Vertex<void, directed_, dummy> {
    using ValueType = void;

    EdgeIterator inEdges();
    EdgeIterator outEdges();
};

// Undirected, with value
template<typename VertexValue, bool directed>
template<typename Value, typename dummy>
struct Graph<VertexValue, directed>::Vertex<Value, false, dummy> {
    using ValueType = Value;

    EdgeIterator edges();

    ValueType value;
};

// Undirected, without value
template<typename VertexValue, bool directed>
template<typename dummy>
struct Graph<VertexValue, directed>::Vertex<void, false, dummy> {
    using ValueType = void;

    EdgeIterator edges();
};

这行得通;但是,我不喜欢转发模板参数的想法,而且它还引入了一个负担,即虚拟模板参数。我还考虑过在 Graph 类之外定义 Vertex 类,如下所示:

template<typename ValueType, bool directed>
class GraphVertex {
    EdgeIterator inEdges();
    EdgeIterator outEdges();

    ValueType value;
};

// Specializations...

template<typename Vertex = GraphVertex<void, false>>
class Graph {
public:
    // ...
private:
    std::vector<Vertex> m_vertices;
};

...甚至像这样:

template<typename ValueType, bool directed>
class GraphVertex {
    EdgeIterator inEdges();
    EdgeIterator outEdges();

    ValueType value;
};

// Specializations...

template<typename VertexValue = void, bool directed = false>
class Graph {
public:
    // ...
private:
    std::vector<GraphVertex<VertexValue, directed>> m_vertices;
};

...但我更喜欢嵌套类的想法,其中 Vertex 类实际上是 Graph 类的属性。另外,即使这三个解决方案中的这个解决方案仍然有我的偏好,我也找不到“GraphVertex”的任何其他名称。这让我很困扰。

最后,这是我的问题。有没有其他方法可以做我想做的事情(即提供不同的 Vertex 类 - 或 features - 取决于 Graph 模板参数)?如果是这样,如何做以及有什么好处;如果不是,我应该更喜欢什么,为什么?

【问题讨论】:

标签: c++ templates c++11


【解决方案1】:

您的第一个实现是不可能直接实现的,因为:

C++11 标准第 14.5.5.3/1 段

[...] 类模板特化是一个独特的模板。类模板部分特化的成员与主模板的成员无关。 [...]

这意味着成员struct Vertex不需要存在于类Graph的部分模板特化中,因此不能直接定义,无需特化整个类。

所以,你不能这样做:

template<typename T1, typename T2>
class Graph { struct Vertex; };

template<typename T2> 
struct Graph<double, T2>::Vertex { };

(另见Template class incomplete specialization)

C++11 标准第 14.7.3/15 段

成员或成员模板可以嵌套在许多封闭类模板中。在此类成员的显式特化中,成员声明之前应为 template&lt;&gt; 对于每个显式特化的封闭类模板

(另见 Specializing inner template with default parameters,如 IdeaHat 所示)

这意味着:

template<typename VertexValue = void, bool directed = false>
struct Graph {
    struct Vertex;
};

// template implementation
template<typename VertexValue, bool directed>
struct Graph<VertexValue, directed>::Vertex {
    using ValueType = VertexValue;
};

// full specialization
template<>
struct Graph<void, false>::Vertex {
    using ValueType = void;
};

工作正常。

解决方法

一种可能的解决方案是将您想要专门化的所有类型放入Graph 的基类中,然后完全专门化这个基类:

template<typename VertexValue, bool directed>
struct BaseGraph;

template<typename VertexValue = void, bool directed = false, 
        typename Base = BaseGraph<VertexValue, directed> >
struct Graph : public Base {
    using Vertex = typename Base::Vertex;
};

// Directed, with value
template<typename VertexValue, bool directed>
struct BaseGraph {
    struct Vertex {
        using ValueType = VertexValue;
    };
};

// Directed, without value
template<bool directed>
struct BaseGraph<void, directed> {
    struct Vertex {
        using ValueType = void;
    };
};

// Undirected, with value
template<typename VertexValue>
struct BaseGraph<VertexValue, false> {
    struct Vertex {
        using ValueType = VertexValue;
    };
};

// Undirected, without value
template<>
struct BaseGraph<void, false> {
    struct Vertex {
        using ValueType = void;
    };
};

但这与我更喜欢使用外部 GraphVertex 类的方法非常相似。

【讨论】:

    猜你喜欢
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    • 2016-01-11
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多