5

任意の型の配列でpDataあるmember を持つクラス テンプレートがあるとします。AxBT

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 ステートメントの前にポインターを作成しようとしましたが、その時点でポインターの型が不明であるため、成功しませんでした。

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

4

4 に答える 4

4

ここでの問題は、X<int>x<char>がまったく無関係な型であることです。

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

いくつかの解決策を見ることができますが、それらは本当に必要なものによって異なります。

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

std::function<void ()>呼び出したいメソッドにa をバインドして保存することもできますが、オブジェクトがまだ「生きている」ことを確認してください (現在のコードには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)
  }
}

実際、決定的な答えを出す知識が不足しています。クラスが「エンティティ」である場合は、おそらく継承を使用する必要があります。それらが「値クラス」に似ている場合は、バリアントの方法がより適している可能性があります。

于 2013-02-20T12:41:34.893 に答える
1

C ++は静的に型付けされた言語です。つまり、コンパイル時にオブジェクトの型を知っている必要があります。この場合、ユーザー入力に基づいて作成されたオブジェクトのタイプに基づいているため、実行時にタイプを知ることはできません。

この問題に対処する最も一般的な方法は、動的多態性を使用することです。動的ポリモーフィズムでは、遅延バインディングを使用して共通のインターフェイスを介して関数が呼び出されます。これは、仮想関数を使用してC++で実現します。例えば:

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変数の動的タイプを使用して印刷できるようになります。仮想関数は、これを可能にする秘訣です。

于 2013-02-20T12:57:43.297 に答える
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 ...
}
于 2013-02-20T13:13:47.617 に答える
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);
} 
于 2013-02-20T12:50:11.460 に答える