2

設定

私は可能な限り物事を分解しようとしているグラフライブラリを持っています、そして私が見つけたそれを説明する最もきれいな方法は次のとおりです: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)を使用して機能することです。それでも、原則として、コンパイル時に決定して、コードをより効率的にしたいと思います。

両方の問題を解決する別の解決策、特に私が持っているものよりもクリーンで優れた解決策がある場合は、それについて聞いてみたいです!

4

2 に答える 2

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を、型特性がtrueかfalseかに基づいてtypetrue_typeまたはそれに基づいて作成できる魔法のおかげで機能します。false_typeアクセスが決して実行されないブランチにあったとしても、そこにないメンバーにアクセスするとコンパイラエラーが発生するため、メタプログラミング(オーバーロードを介したブランチ)が必要です。

私はそれがまったく理にかなっていることを願っています、iPodTouchでこのトピックへの答えを入力することは非常に難しいです...

于 2011-11-18T01:40:47.043 に答える
1

まず第一に、私は適切なタイミングで使用されると多重継承の大ファンです。したがって、設計が簡単になる場合は、それを使用してください。dynamic_cast <>を削除し、コンパイル時の選択を簡単に置き換えることができます。オーバーロードされた関数を使用して、切り替えを行います。型で何も役に立たないときにvoid*をとる関数と、指定された型で何か役に立つことをする関数があります。コードは次のようになります。

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;
    }
};
于 2011-11-18T01:35:36.737 に答える