模板,接口(多重继承)和静态函数(命名构造函数)

时间:2011-11-18 01:03:47

标签: c++ templates multiple-inheritance named-constructor

设置

我有一个图库,我试图尽可能地分解事物,我发现的最简洁的描述方式如下:有一个vanilla类型node只实现了一个边缘列表:

class node
{
   public:
      int* edges;
      int edge_count;
};

然后,我希望能够为这整个组合添加接口,如下所示:

template <class T>
class node_weight
{
   public:
      T weight;
};

template <class T>
class node_position
{
   public:
      T x;
      T y;
};

等等。然后,实际的图类进来,它是根据实际的节点类型进行模板化的:

template <class node_T>
class graph
{
   protected:
      node_T* nodes;

   public:
      static graph cartesian(int n, int m)
      {
         graph r; 

         r.nodes = new node_T[n * m];

         return r;
      }
};

扭曲是它命名构造函数,构造一些特殊的图形,如笛卡尔格子。在这种情况下,我希望能够在图表中添加一些额外的信息,具体取决于node_T实现的接口。

实现这一目标的最佳方法是什么?

可能的解决方案

通过dynamic_cast<>

,我想到了以下简单的解决方案
template <class node_T, class weight_T, class position_T>
class graph
{
   protected:
      node_T* nodes;

   public:
      static graph cartesian(int n, int m)
      {
         graph r;

         r.nodes = new node_T[n * m];

         if (dynamic_cast<node_weight<weight_T>>(r.nodes[0]) != nullptr)
         {
            // do stuff knowing you can add weights
         }

         if (dynamic_cast<node_position<positionT>>(r.nodes[0]) != nullptr)
         {
            // do stuff knowing you can set position
         }

         return r;
      }
};

将在以下node_T上运行:

template <class weight_T, class position_T>
class node_weight_position : 
      public node, public node_weight<weight_T>, public node_position<position_T>
{
    // ...
};

问题

这是哲学上的 - 正确的方法吗?我知道人们对多重继承看起来不太好,虽然像这样的“接口”应该都很好。

不幸的是,这有问题。据我所知,dynamic_cast<>涉及相当多的运行时开销。因此,我遇到了我之前解决的问题:编写需要权重的图算法,而不管实际的node_T类是否具有权重。采用这种“接口”方法的解决方案是编写一个函数:

template <class node_T, class weight_T>
inline weight_T get_weight(node_T const & n)
{
   if (dynamic_cast<node_weight<weight_T>>(n) != nullptr)
   {
      return dynamic_cast<node_weight<weight_T>>(n).weight;
   }

   return T(1);
}

但它的问题在于它使用运行时信息(dynamic_cast),但原则上我想在编译时决定它,从而使代码更有效。

如果有一个不同的解决方案可以解决这两个问题,特别是一个比我更清洁和更好的问题,我很乐意听到它!

2 个答案:

答案 0 :(得分:3)

类型特征怎么样?如果您有一个支持C ++ 11部分的编译器,std::is_base_of标题中有<type_traits>

如果不这样做,则会有相同类型特征的提升。

现在,要真正能够使用它,你需要一些元编程:

// in the class...
//  branch on whether the node type has weights
static void set_weights(node_T* nodes, std::true_type){
    // has weights, set them
    // ...
}

static void set_weight(node_T* nodes, std::false_type){
    // doesn't have weights, do nothing
}

// in the function...
typedef std::is_base_of<node_weight<weight_T>, node_T>::type has_weights;
set_weight(nodes, has_weights());

这要归功于一些魔法,它允许嵌套的typedef typetrue_typefalse_type,具体取决于类型特征是真还是假。我们需要元编程(通过重载进行分支),因为访问不存在的成员将导致编译器错误,即使访问位于永远不会执行的分支中。

我希望这完全没有意义,在iPod Touch上输入这个主题的答案很难......

答案 1 :(得分:1)

首先,我在适当的时候使用多重继承的忠实粉丝。因此,如果它使您的设计更简单,那么使用它。至于摆脱dynamic_cast&lt;&gt;并用一个简单的编译时选择替换它。您只需使用重载函数为您进行切换。你有一个函数,当你不能对类型和对指定类型做一些有用的函数做任何有用的事情时,它取无效*。您的代码如下所示。

template <class node_T, class weight_T, class position_T>
class graph
{
protected:
    node_T* nodes;

private:
    static void do_stuff_with_weights(graph& r, void* /*dummy*/)
    {
    }

    static void do_stuff_with_weights(graph& r, node_weight<weight_T>* /*dummy*/)
    {
        // do stuff knowing you can add weights
    }

    static void do_stuff_with_pos(graph& r, void* /*dummy*/)
    {
    }

    static void do_stuff_with_pos(graph& r, node_position<position_T>* /*dummy*/)
    {
            // do stuff knowing you can set position
    }

public:
    static graph cartesian(int n, int m)
    {
        graph r;

        r.nodes = new node_T[n * m];

        do_stuff_with_weights(r, (node_T*) 0);
        do_stuff_with_pos(r, (node_T*) 0);

        return r;
    }
};