8

文字列を値に関連付ける単純なテンプレート構造体があります

template<typename T> struct Field
{
    std::string name; T self;
}

任意のタイプの 1 つ以上のフィールドを受け入れたい関数があり、フィールドは異なるタイプの可能性があるstd::initializer_listため、C++ を使用しているのは、私の知る限り、型付きの可変引数がなく、可変長引数のサイズであり、どこから開始するかを決定するために少なくとも 1 つの他の引数が必要です。

問題は、異なるタイプのフィールドを受け入れるように指示する方法がわからないことです。Java では を使用するだけfoo(Field<?> bar, Field<?>... baz)ですが、C++ には型付き可変引数とワイルドカードの両方がありません。私の他の唯一のアイデアは、 type のパラメーターを作成すること std::initializer_list<Field<void*>>ですが、それは悪い解決策のようです...それを行うためのより良い方法はありますか?

4

4 に答える 4

9

いくつかのこと...

  • C ++ 11(あなたが話しているので持っているようですstd::initializer_list)には、型付きの可変引数があり、特に、可変引数テンプレートと呼ばれます

  • Java ジェネリックと C++ テンプレートはまったく別物です。Javaジェネリックは、参照を格納する単一の型を作成Objectし、インターフェース内の型への自動キャストインおよびキャストアウトを提供しますが、重要なのは、型消去を実行することです。

解決したい問題を説明し、C++ で慣用的な問題の解決策の提案を得ることをお勧めします。Java の動作を本当に真似したい場合 (言語が異なり、イディオムが異なるだけでは十分とは言えません)、C++ で型消去を手動で使用できます(つまり、 を使用しますboost::any)。しかし、プログラムで完全な型消去の必要性を感じることはめったにありません... バリアント型 ( boost::variant) を使用することはもう少し一般的です。

コンパイラが可変個引数テンプレートをサポートしている場合 (すべてのコンパイラがサポートしているわけではありません)、いつでもそれで遊ぶことができますが、型消去を使用しない限り、後でベクトルにフィールドを格納することは、完全に一般的なアプローチでは少し複雑になる可能性があります。(繰り返しますが、解決すべき問題は何ですか?もっと簡単な解決策があるかもしれません...)

于 2013-02-28T04:47:17.543 に答える
4

Java ジェネリックは、C++ テンプレートよりもboost::any、単に変数に a を詰め込むことに近いです。self試してみてください。C++ テンプレートは、既定では相互に実行時または動的な関係を持たない型を作成します。

pImplこのような関係を手動で導入することができます。たとえば、共通の親とタイプの消去とスマート ポインターの賢明な使用を介してです。

C 型の可変引数は、C++11 のスタイルから外れています。可変引数テンプレート引数は、コンパイラがそれらをサポートしている限り、非常にタイプ セーフです (MSVC 2012 の 2012 年 11 月の CTP は、clang や gcc の古いバージョンと同様に、それらをサポートしています (更新 1、CTP ではありません))。

C++ のテンプレートは一種のメタプログラミングであり、Java Generics よりもプログラムを作成するプログラムを作成することに近いです。Java Generic には 1 つの共有「バイナリ」実装がありますが、C++ テンプレートの各インスタンスは完全に異なる「プログラム」(COMDAT フォールディングなどの手順を介して 1 つのバイナリ実装に縮小できます) であり、その詳細はテンプレートによって記述されます。コード。

 template<typename T>
 struct Field {
   T data;
 };

は、「フィールド タイプの作成方法は次のとおりです」という小さなプログラムです。andを渡すintdouble、コンパイラは大まかに次のように処理します。

 struct Field__int__ {
   int data;
 };
 struct Field__double__ {
   double data;
 };

そして、これら 2 つの型の間で変換できるとは思わないでしょう。

一方、Java ジェネリックは次のようなものを作成します。

struct Field {
  boost::any __data__;
  template<typename T>
  T __get_data() {
    __data__.get<T>();
  }
  template<typename T>
  void __set_data(T& t) {
    __data__.set(t);
  }
  property data; // reading uses __get_data(), writing uses __set_data()
};

whereboost::anyは任意の型のインスタンスを保持できるコンテナーであり、dataフィールドへのアクセスはそれらのアクセサーを介してリダイレクトされます。

C++ は、テンプレート メタプログラミングを使用して、Java ジェネリックと同等のものを作成する手段を提供します。Java で C++ テンプレートのようなものを作成するには、Java プログラムでカスタム Java バイトまたはソース コードを出力し、コードをソースとして書き込むコードにデバッガーが接続できるようにそのコードを実行する必要があります。バグの。

于 2013-02-28T04:09:11.883 に答える
2

C++ テンプレートではワイルドカードを使用する必要はありません。C++ では常に型が認識され、Java のように「消去」されないためです。void foo(Field<?> bar, Field<?>... baz)C++ でメソッド (または関数) を記述するには、次のように記述します。

 template<class T, class... Ts>
 void foo(Field<T> bar, Field<Ts>... baz);

それぞれField<Ts>が異なるタイプである可能性があります。関数内で可変引数を使用するには、単にbaz.... 別の関数を呼び出したいとします。

 template<class T, class... Ts>
 void foo(Field<T> bar, Field<Ts>... baz)
 {
     foo2(baz...);
 }

で型を拡張することもできるField<Ts>...ので、タプルに入れたい場合 (異なる型になる可能性があるため、配列に入れることはできません):

 template<class T, class... Ts>
 void foo(Field<T> bar, Field<Ts>... baz)
 {
     std::tuple<Field<Ts>...> data(baz...);
 }
于 2013-02-28T15:23:35.883 に答える
-1

これは、C++ ではあまり慣用的ではありません。おそらく、それは可能です。コプリエンの本にはいくつかのアイデアがあるかもしれません。しかし、C++ は型付けを信じているため、強く型付けされています。それを Smalltalk に変えようとしたり、キジのように折りたたんだりしようとすると、涙が出てくるかもしれません。

于 2013-02-28T03:59:03.903 に答える