2013-02-20 11 views
5

メンバーがpDataであり、AxBの任意のタイプの配列Tであるクラステンプレートがあるとします。if文からクラステンプレートのインスタンスを取得する方法は? (C++)

template <class T> class X{ 
public: 
    int A; 
    int B; 
    T** pData; 
    X(int a,int b); 
    ~X();   
    void print(); //function which prints pData to screen 

}; 
template<class T>X<T>::X(int a, int b){ //constructor 
    A = a; 
    B = b; 
    pData = new T*[A]; 
    for(int i=0;i<A;i++) 
     pData[i]= new T[B]; 
    //Fill pData with something of type T 
} 
int main(){ 
    //... 
    std::cout<<"Give the primitive type of the array"<<std::endl; 
    std::cin>>type; 
    if(type=="int"){ 
     X<int> XArray(a,b); 
    } else if(type=="char"){ 
     X<char> Xarray(a,b); 
    } else { 
     std::cout<<"Not a valid primitive type!"; 
    } // can be many more if statements. 
    Xarray.print() //this doesn't work, as Xarray is out of scope. 
} 

例として、Xarrayはif文の内部で構築されるため、他の場所では使用できません。 if文の前にポインタを作成しようとしましたが、その時点でポインタの型が不明なため、成功しませんでした。

この種の問題に対処する適切な方法は何でしょうか?

+0

C++が静的に型付けされているため、「明白な」答えはありません。あるタイプのユーザにプロンプ​​トを出すことはできません。そのタイプを作成して他の場所で使用することはできません。コンパイル時にタイプを知る必要があります!この状況に一般的に対処する1つの手法は「タイプ消去」ですが、そのためには、すべてのタイプに共通の要素を指定し、その共通要素(「印刷可能」など)を介してのみインタフェースする必要があります。 –

+0

ここで、[this](http://stackoverflow.com/questions/1984492/runtime-determine-type-for-c)が役立つかもしれません。 –

答えて

1

C++を使用すると、コンパイル時にオブジェクトの種類を知っていなければならないことを意味し、静的型付け言語です。この場合、ユーザー入力に基づいて構築されたオブジェクトの型を基にしているため、実行時に型を知ることはできません。

この問題を解決する最も一般的な方法は、を使用して共通インターフェイス経由で関数を呼び出すdynamic polymorphismを使用することです。これはC++でvirtual functionsを使って行います。例:

struct IPrintable { 
    virtual void print() = 0; 
}; 

template<class T> 
class X : public IPrintable { 
    // Same code as you showed above. 
}; 

int main() { 
    std::cout<<"Give the primitive type of the array"<<std::endl; 
    std::cin>>type; 

    std::unique_ptr<IPrintable> XArray; 

    if(type=="int"){ 
     XArray.reset(new X<int>(a,b)); 
    } else if(type=="char"){ 
     XArray.reset(new X<char>(a,b)); 
    } else { 
     std::cout<<"Not a valid primitive type!"; 
    } // can be many more if statements. 

    Xarray->print() // this works now! 
} 

これにより、範囲外の問題が解決され、XArray変数の動的タイプを使用して印刷することができます。バーチャルファンクションはこれを可能にする秘密のソースです。

+0

継承だけが可能な解決策ではありません。他の言語では選択肢がないかもしれませんが、C++ではこの結果を達成する方法がたくさんあります。 2つのオブジェクトが共通の属性を共有しているという理由だけで、基本クラスを作成し、継承を強制すると、私にとっては極端なようです。言い換えれば、型が "エンティティクラス"になり、 "値クラス"(それらを簡単にコピーすることができず、比較がより複雑になる)を防ぐことができます。 – ereOn

+0

私はそれが唯一の選択肢であることを暗示するつもりはありませんでした。しかし、通常、他の要因が関係しない場合は最も簡単な選択肢です。 OPの正確な質問があれば、コールバックや訪問者のようなもっと複雑なものを使用する理由はありません。また、スライスすることを心配する必要がないので、基本クラスがすべて空のインターフェイスであれば、コピーに関する問題はありません。 –

+0

私はそれが悪い解決策であると言っていませんでした。我々は知ることができません(OPは、彼が直面しているすべての制約について私たちに語っているかもしれません)。ビジターは複雑ではありません。それは実際に私が今まで見たことのない最も愚かなものの一つです。 C++を学ぶ人々は、オブジェクト指向のプログラミングを継承することが唯一の解決策であると考えるよう勧められないはずです。毎週、数十人のJavaプログラマーがC++に来ていると思っています。それは本当に退屈です。 – ereOn

4

ここでの問題は、X<int>x<char>は完全に無関係のタイプです。

これらが両方とも同じテンプレートクラスの結果であるという事実は、ここでは役に立ちません。

私はいくつかの解決策を見ることができますが、実際に必要なものに依存します。

例えば、X<>インスタンスは、print()メソッド(最終的に純粋な仮想)を持つ共通のテンプレート化されていない基本クラスから派生させることができます。しかし、これを行う前に、機能レベルで意味をなさないことを確認してください。技術的な制約のためにだけではなく、理にかなっているため、継承を使用する必要があります。そして、そうするならば、おそらく仮想デストラクタも必要になるでしょう。

あなたはまた、彼らはあなたの現在のコードではありません(結合して、あなたが呼び出したいメソッドにstd::function<void()>を格納しますが、オブジェクトがまだ「生きて」いることを確認することができます:両方X<int>X<char>が破壊され、彼らが行くときあなたが実際にprint()と呼ぶ前に、範囲外です)。

最終的な解決策は、X<int>X<char>boost::variant<>が役に立ちます)の両方に対応するバリアント型を作成することです。次に、タイプごとにprint()の機能を実装しているビジターを書くことができます。最後の解決策をピッキング

、それはのようなものになるだろう:私たちは、実際に決定的な答えを与えるために、知識が不足している

typedef boost::variant<X<int>, X<char>> genericX; 

class print_visitor : public boost::static_visitor<void> 
{ 
public: 
    template <typename SomeType> 
    void operator()(const SomeType& x) const 
    { 
     // Your print implementation 
     // x is your underlying instance, either X<char> or X<int>. 
     // You may also make several non-templated overloads of 
     // this operator if you want to provide different implementations. 
    } 
}; 

int main() 
{ 
    boost::optional<genericX> my_x; 

    if (type=="int") { 
    my_x = X<int>(a,b); 
    } else if(type=="char") { 
    my_x = X<char>(a,b); 
    } 

    // This calls the appropriate print. 
    if (my_x) { 
    boost::apply_visitor(print_visitor(), *my_x) 
    } 
} 

:あなたのクラスは、「実体」であれば、あなたはおそらく、継承のために行く必要があります。彼らが「価値クラス」のようなものであれば、その変種の方がより適しているかもしれません。

1

異なる配列で作業する場合は、テンプレートの種類によってテンプレートだけでは役に立ちません。現在、X<int>X<char>の間には全く関係がありません。

一般的な型の2つのサブタイプとして扱う場合は、継承(および動的に割り当てられた変数)を使用する必要があります。たとえば、すべてのX<T>は同じ基本クラスを継承することができる、Printableを言うと、あなたはunique_ptr<Printable>にデータを格納することができます

unique_ptr<Printable> r; 
if(type=="int"){ 
    r.reset(new X<int>(a,b)); 
} else if(type=="char"){   
    r.reset(new X<char>(a,b); 
} 
r->print(); 

しかし、これはおそらく最高のデザインではありません。

おそらくより良い解決策は、ifの外側で作業しようとする代わりに、ifの内部のすべての作業を移動することです。あなたの単純な例では、これはprintへの呼び出しを複製することによって行うことができますが、これはどちらかというとあまり良いものではありません。しかし、この考え方に向かって行く、私たちは仕事をしてテンプレート関数を作成することができます

template<class T> 
void workWithType(int a, int b) 
{ 
    X<T> Xarray(a, b); 
    Xarray.print(); 
} 

//... 

if(type=="int"){ 
    workWithType<int>(a,b); 
} else if(type=="char"){ 
    workWithType<char>(a,b); 
} 
+0

継承は必ずしも動的割り当てを使用する必要があるとは限りません。 – ereOn

1

よりもむしろ、私は提案の残りの部分よりも逆の道を行くだろうmainにテンプレートに合うようにしようと... mainの外とに対処する必要があり、それ自身の(おそらくテンプレート)関数にコードを移動

template <typename T> 
void generateAndPrint(int a, int b) { 
    X<T> x(a,b); 
    x.print(); 
} 
int main() { ... 
    if (type=="int") generateAndPrint<int>(a,b); 
    else if (type=="char") generateAndPrint<char>(a,b); 
    else ... 
} 
関連する問題