web-dev-qa-db-ja.com

C ++バーチャルテンプレートメソッド

私は抽象クラスを持っています(この方法ではコンパイルされないことは知っていますが、私がやりたいことを理解するためのものです):

_class AbstractComputation {
    public:
        template <class T> virtual void setData(std::string id, T data);
        template <class T> virtual T getData(std::string id);
};

class Computation : public AbstractComputation {
    public:
        template <class T> void setData(std::string id, T data);
        template <class T> T getData(std::string id, T data);
};
_

したがって、setData<double>("foodouble", data)を呼び出すとき、foodouble(ここでは主な関心事ではない内部メカニズム)で識別されるdoubleをdoubleデータに設定する必要があります。

それをどうやってやるの?

virtual void setData<double>(std::string id, double data)のようなものを入力することで意味があるかもしれないと思いますが、どうすればいいのかわかりません。

33
Vincent

問題は、静的時間ポリモーフィズム(テンプレート)とランタイムポリモーフィズムを簡単に混在させることができないことです。あなたの例で特定のコンストラクトを言語が許可しない理由は、テンプレートメンバー関数をインスタンス化できる潜在的に無限の異なるタイプがあり、そのため、コンパイラがそれらの多くのタイプを動的にディスパッチするコードを生成する必要があることを意味します実行不可能です。

制限を回避するためにここで実行できるさまざまなことがあります。基本的には、静的または動的なポリモーフィズムを取り除きます。動的なポリモーフィズムを方程式から削除するには、派生していない型を提供して、<key,value>マッピング、およびそれをベースレベルでのみ解決するテンプレートを提供します。

class AbstractComputation {
public:
   template <typename T>
   void setData( std::string const & id, T value ) {
      m_store.setData( id, value );
   }
   template <typename T>
   T getData( std::string const & id ) const {
      return m_store.getData<T>( id );
   }
protected:
   ValueStore m_store;
};

派生クラスはベースからValueStoreにアクセスできるようになり、ポリモーフィズムは不要になりました。 (これはAbstractComputationに機能を直接実装することでも可能ですが、懸念事項を分離することはおそらく意味があります)

もう1つのオプションは、ランタイムポリモーフィズムを維持し、静的ポリモーフィズムを削除することです。これは、基本クラスで型消去を実行し、type-erased引数を取る適切な(テンプレート化されていない)関数にディスパッチすることで実行できます。これの最も簡単なバージョンは、単にboost::any

class AbstractComputation {
public:
   template <typename T>
   void setData( std::string const & id, T value ) {
      setDataImpl( id, boost::any( value ) );
   }
   template <typename T>
   T getData( std::string const & id ) const {
      boost::any res = getDataImpl( id );
      return boost::any_cast<T>( res );
   }
protected:
   virtual void setDataImpl( std::string const & id, boost::any const & value ) = 0;
   virtual boost::any getDataImpl( std::string const & id ) const = 0;
};

型消去が内部でどのように実装されるかは興味深いですが、ここでは範囲外であるため、重要な部分はboost::anyは、引数に対して型消去を使用することで内部的にany型を格納できる具象(非テンプレート)型であり、同時にデータの型保証された取得を可能にします。

場合によっては、テンプレートをメソッドレベルからクラスレベルに移動するだけで十分な場合があります。例:

#include <iostream>

template<typename T>
class AbstractComputation {
public:
    virtual void setData(std::string id, T data)
    {
        std::cout << "base" << std::endl;
    }
};

template<typename T>
class Computation : public AbstractComputation<T> {
public:
    virtual void setData(std::string id, T data)
    {
        std::cout << "derived" << std::endl;
    }
};

int main()
{
    AbstractComputation<int> *x = new Computation<int>();

    x->setData("1", -1);

    delete x;
    return 0;
}
14
TobiMcNamobi

おそらく boost::any あなたの場合。

virtual void setData(std::string id, boost::any data);

ほとんどすべてをカプセル化できるラッパーです。

この回答の同様のトピックに関する詳細情報

3
jpalecek

まず、virtualテンプレート関数を使用できません。コンパイル時にテンプレートが解決されるため、virtualは機能しません。コンパイラが選択するテンプレートを認識しないためです。詳細については、 here を参照してください。

2
Tony The Lion

使用する boost::anyを選択してデータムを受け入れ、実際に設定したら、正しいデータ型を取得します。

1
Nim

可能なタイプのリストを事前に知っている場合、プリプロセッサが役立ちます。

#define MY_CLASSES MYTYPE(int) MYTYPE(float) MYTYPE(double)

class AbstractComputation {
    public:
#     define MYTYPE(T) virtual void setData(std::string id, T data)=0;\
                       virtual void getData(std::string id, T& dst_data)=0;
      MY_CLASSES
#     undef MYTYPE
};

class Computation : public AbstractComputation {
    public:
#     define MYTYPE(T) virtual void setData(std::string id, T data){std::cout<<"writing: "<<data<<std::endl;}\
                       virtual void getData(std::string id, T& dst_data){dst_data=0;/*put your actual implementation here*/}
      MY_CLASSES
#     undef MYTYPE
};

可能なタイプの完全なリストがわからない場合は、おそらく、問題は解決できません。他の人が述べたように、型の消去も役立つ場合がありますが、すべての状況ではそうではありません。

1
user396672