web-dev-qa-db-ja.com

未知の型を仮想関数に渡すC ++

私はC++で書いていますが、未知の型(実行時にのみ知られる)を純粋仮想関数に渡したいと思います。

virtual void DoSomething(??? data);

ここで、DoSomethingは、派生クラスでの純粋仮想関数の実装です。

テンプレートを使用する予定でしたが、仮想関数とテンプレートが一緒に機能しないことが判明しました: C++クラスのメンバー関数テンプレートを仮想にすることはできますか?

関数に渡すすべてのクラスに基本クラスを使用することは避けたいと思います(C#の object のようなもの)。

前もって感謝します

17
Avner Gidron

型消去 が必要です。この例は、汎用 boost::any (およびC++ 17ではstd::any)です。

virtual void DoSomething(boost::any const& data);

そして、各サブクラスは、期待するデータを取得するためにsafeany_castを試行できます。

void DoSomething(boost::any const& data) {
  auto p = any_cast<std::string>(&data);

  if(p) {
    // do something with the string pointer we extracted
  }
}

もちろん、求める動作の範囲がより制限されている場合は、独自の型消去抽象化を展開できます。

Boost/C++ 17を使用したくない場合は、基本クラスから「doSometing」関数のパラメーターを取得し、適切なクラスオブジェクトに動的キャストすることを検討してください。この場合、実行時に有効なポインタを取得したことを確認できます。

class param{
public:
    virtual ~param(){};
};

template <typename T>
struct specificParam:param{
    specificParam(T p):param(p){}
    T param;
};


class Foo
{
public:
    virtual void doSomething(param* data) = 0;
};

template <typename T>
class Bar : public Foo
{
public:
    virtual void doSomething(param* data){
        specificParam<T> *p = dynamic_cast<specificParam<T> *>(data);

        if (p != nullptr){
            std::cout<<"Bar got:" << p->param << "\n";
        }
        else {
            std::cout<<"Bar: parameter type error.\n";
        }
    }
};

int main(){
  Bar<char>   obj1;
  Bar<int>    obj2;
  Bar<float>  obj3;

  specificParam<char>   t1('a');
  specificParam<int>    t2(1);
  specificParam<float>  t3(2.2);

  obj1.doSomething(&t1); //Bar got:a
  obj2.doSomething(&t2); //Bar got:1
  obj3.doSomething(&t3); //Bar got:2.2

  // trying to access int object with float parameter
  obj2.doSomething(&t3); //Bar: parameter type error.
}

最も簡単な(しかし安全ではない!)方法は、void *ポインター+静的キャストを使用することです。

class Foo
{
public:
    virtual void doSomething(void* data) = 0;
};

template <typename T>
class Bar:public Foo
{
public:
    virtual void doSomething(void* data){
        T* pData = static_cast<T*>(data);
        std::cout<<"Bar1 got:" << *pData << "\n";
    }
};

int main(){

  Bar<char>  obj1;
  Bar<int>   obj2;
  Bar<float> obj3;

  char  c = 'a';
  int   i = 1;
  float f = 2.2;

  obj1.doSomething(&c); // Bar1 got:a
  obj2.doSomething(&i); // Bar1 got:1
  obj3.doSomething(&f); // Bar1 got:2.2

  //obj2.doSomething(&c); // Very bad!!!     
}
2
Nir

型消去だけが可能性ではありません。

ビジターパターンを使用することに興味があるかもしれません:引数としてstd :: Variantを取り、実装したいテンプレートコードを含むラムダでそれを訪問します:

virtual void doSomething(std::variant<int,float/*,...*/> data)
   {
   visit([=](auto v){/*...*/;},data);
   }
1
Oliv

そんな感じ?:

class Foo
{
    virtual ~Foo() = 0;
};

template <typename T>
class Bar : public Foo
{
    T object;
}

...

virtual void DoSomething(Foo* data)
{
    Bar<int>* temp = dynamic_cast<Bar<int>*>(data);
    if (temp)
         std::count<<temp->object;
}
0
gotocoffee