2016-12-15 5 views
2

私はC++のクラステンプレートの概念を理解していると思っていましたが、私のコードを見れば、もはやそれほど確かではありません。クラステンプレートとその型を扱うC++の方法は何ですか?

:私は、例えば、異なるタイプを使用して、 MyChildのインスタンスとコンテナを持っている私のコードで今すぐ

class MyParent 
{ 
    std::string m_Name; 
    MyParent(std::string Name) : m_Name(Name) { } 
    virtual const std::type_info& GetType() = 0; 
}; 

template <class T> 
class MyChild : public MyParent 
{ 
    T m_Var; 
    const std::type_info& GetType() override 
    { return typeid(m_Var); } 
}; 

:私はこのような二つのクラス、非テンプレート親と子としてテンプレートを、持っていると言います

MyParent* p1 = new MyChild<int>("one"); 
MyParent* p2 = new MyChild<float>("two"); 
MyParent* p3 = new MyChild<double>("three"); 
std::vector<MyParent*> v = {p1, p2, p3}; 

これまでのところ、これは明らかです。私の混乱が始まるところです。私はこのベクトル上で反復する関数を持っているとし、各要素でいくつかのことをする必要があるとします。いくつかの例で私は(私の質問は、コード自体に関するものではありませんが、そのような状況に対処する方法を)作っ:私は一度だけ、このコードブロックを持つことになり、たとえば

Pythonのような他の言語で
/* ... */ 
for(auto* p : v) 
{ 
    if(p->GetType() == typeid(int)) 
    { 
     int val = p->m_Var; 
     std::list<int> lst = {val, 1, 2, 3}; 
     if(val >= 0) 
      SomeTemplFuncPositive(v * v, lst); // a template variadic function 
     else 
      SomeTemplFuncNegative(v * v * -1, lst); 
    } 
    else if(p->GetType() == typeid(float)) 
    { 
     /* ... now the same block c&p again for float? */ 
    } 
    else if(p->GetType() == typeid(double)) 
    { 
     /* ... and again for double?! */ 
    } 
} 
/* ... */ 

が、 C++では、floatのコードブロック全体をもう一度コピーし、doubleの別の時間をコピーする必要があると思われます。

C++を非難したくありませんそうでなければOKです。私はちょうど疑問に思っています、これは本当に一般的に正しいアプローチですか?調整する必要が-ity

+0

テンプレートに問題はありませんが、オブジェクトには問題はありません。 'typeid'のデザインは醜いです。なぜ多型が存在するのか。 – Stargateur

+1

'if(p-> GetType()== typeid(int))'は手続き型コーディング(および思考)を導入しています。 '* p'のクラスの中の' if'ブロックからすべてのコードを移動してください。必要に応じて 'MyChild 'を拡張してください。 – axiac

+4

子クラスが異なる振る舞いをし、型で分岐する必要があるということは、デザインの欠陥を意味します。あなたは本当に解決しようとしている問題は何ですか?それは、XY問題です。問題を解決するために間違った解決方法を選択しましたが、今はインターネットがどのように続行するかを尋ねる時点まで言語が闘っています。私がこの点に着くたびに、それは常に私をそこに導き出し、(根本的に)私のアプローチを変えて、多くのものを改善した解決策の中で、いくつかの後ろ向きの選択です。ちょうど私の2セント... – rubenvb

答えて

6

あなたvirtual

class MyParent 
{ 
    std::string m_Name; 
    MyParent(std::string Name) : m_Name(Name) { } 
    virtual void doSomething()=0; 
}; 

template <class T> 
class MyChild : public MyParent 
{ 
    T m_Var; 
    void doSomething() override 
    { 
     T val = this->m_Var; 
     std::list<T> lst = {val, 1, 2, 3}; 
     if (val >= 0) 
      SomeTemplFuncPositive(v * v, lst); 
     else 
      SomeTemplFuncNegative(v * v * -1, lst); 
    } 
}; 

は、あなたがその基底クラスを通じてdoSomething()を起動します。

for(auto* p : v) 
{ 
    p->doSomething(); 
} 

すべてが正確に一度だけ書かれています。

+0

これは' MyChild'が 'SomeTemplFuncPositive()'と 'SomeTemplFuncNegative()'関数に直接アクセスできると仮定しています。元のコードでは、関数は 'MyChild'の外にありました。必要であれば、関数ポインタを 'doSomething()'に渡すだけで、何を呼び出すべきかがわかります。 –

0

BaseからChildにキャストする必要がある場合は、コードが間違っている可能性があります(少なくともC++では)。デリバティブへのポインタを格納している間は、特定のメンバーにパブリックアクセスする必要はありません(ただし、仮想メソッドでもそうすることができます)。この場合、リストは微分で宣言し、Tをテンプレートパラメータとします。

また、あなたがのGetType機能を必要としない、あなたはdynamic_cast<DerivativeClass*>(baseClassPtr)を使用することができ、鋳造がOKであれば、それは適切なポインタを返し、nullptr鋳造場合は行うことはできません。

関連する問題