7

私はこのクラステンプレートを持っています:

template<class... T>
class Test {
  std::vector<TestCase*> test_cases;
public:
  Test() {
    // Here, for each T an instance should be added to test_cases.
    test_cases.push_back((new T)...);
  }
};

これは1つのテンプレート引数では問題なく機能しますが、複数の引数では次のエラーが発生します。

error: too many arguments to function call, expected 1, have 2

この方法で可変個引数テンプレートを使用するにはどうすればよいnewですか?正しい構文は何ですか?


編集:私の質問は完全に明確ではなかったと思います。私が欲しいのはこれです:

Test<TestCase1, TestCase2, TestCase3>;
// The constructor will then be:
test_cases.push_back(new TestCase1);
test_cases.push_back(new TestCase2);
test_cases.push_back(new TestCase3);

私のコンパイラはclang163.7.1で、このフラグは次のとおり-std=c++0xです。

4

6 に答える 6

3

vector::push_back1つのパラメーターが必要なため、関数呼び出しで可変個引数テンプレートを展開できません。また、基本クラス(他のすべてのクラスの派生元)のテンプレートパラメーターを追加しました。

これがコンパイルするものです。

struct base{};
struct d0 : base{};
struct d1 : base{};
struct d2 : base{};

#include <vector>

// termination condition for helper function
template <class T>
void add(std::vector<T*>&) { 
}

// helper function
template <class T, class Head, class... Tail>
void add(std::vector<T*>& v) { 
       v.push_back(new Head()); 
       add<T, Tail...>(v);
}

template <class T, class ... U>
class test
{
    std::vector<T*> vec;
public:
    test() {
       add<T, U...>(vec);      
    }
};

int main() 
{
    test<base, d0,d1,d2> t;
}
于 2011-09-18T20:13:15.337 に答える
2

パックの拡張は、選択した数の状況でのみ発生する可能性があり、任意の式またはステートメントでは機能しません。ただし、これらの状況の1つはリストの初期化であり、操作の順序はリストの初期化構文のブレース初期化子に対して定義されているため、任意のステートメントを展開することは常に可能です。ウィットに:

typedef std::initializer_list<int> expand;
expand { ( test_cases.push_back(new T), void(), 0 )... };

void()秘訣は、オーバーロードされたの呼び出しを抑制することですoperator,。ここではまったく関係ありませんが、マクロの機能をリファクタリングするときに役立つ可能性があるため、ここに含めました。

#define EXPAND( exp ) \
    std::initializer_list<int> { ( (exp), void(), 0 )... }

// No use of '...', it's in the macro body
EXPAND(( test_cases.push_back(new T) ));
于 2011-09-18T20:00:47.537 に答える
2

これは達成できますが、式を直接記述するため、少し回り道になります。push_back可変個引数テンプレートの引数リストの引数ごとに1回呼び出す必要があります。

これをどのように達成しますか?テンプレート引数ごとに1回再帰関数を呼び出すと、次のようになります。

template <typename Base, typename T1, typename T2, typename... T>
void fill(std::vector<Base*>& vec) {
    vec.push_back(new T1);
    fill<Base, T2, T...>(vec);
}

template <typename Base, typename T1>
void fill(std::vector<Base*>& vec) {
    vec.push_back(new T1);
}

ここでは、fill関数の2つのオーバーロードがあります。1つは可変個引数テンプレートの引数リストがあり、もう1つはありません。これは再帰の基本ケースです。少なくとも2つのテンプレート引数がまだある限り、最初のバージョンが呼び出されます。引数が1つしかない場合は、代わりに2番目の引数が呼び出されます。

コンストラクターで次のように呼び出します。

fill<TestCase, T...>(test_cases);
于 2011-09-18T20:03:34.760 に答える
1

関連する注意点として、この特定のケースではinitializer_list、コンストラクターを次のように記述することで、vectorのサポートを使用できます。

Test()
:test_cases{ new T ... }
{ }

または、何らかの理由でコンストラクタ初期化子を使用できない場合は、割り当てを使用します

Test() {
  test_cases = { new T ... };
}
于 2011-09-18T20:32:47.100 に答える
0

std :: vector内にタプルが必要な場合がありますか?これが意図したものかどうかはわかりませんが、少なくとも私のG ++ 4.6.1でコンパイルされます:D

#include <vector>
#include <utility>
#include <functional>
#include <string>

template<class... T>
class Test {
  std::vector<std::tuple<T*...>> test_cases;
public:
  Test() {
    // Here, for each T an instance should be added to test_cases.
    test_cases.push_back(std::tuple<T*...>((new T)...));
  }
};

int main()
{
   Test<int, float> foo;
   Test<std::string, double> bar;
}
于 2011-09-18T19:24:10.407 に答える
0

テンプレートベクトルとは対照的に、任意のタイプの動的ベクトルが必要だと思います(個人的には見ていませんが、友人からブーストライブラリにこのようなものがあると言われました)。

テンプレートベクトルは基本的に、1つの定義されたタイプ(すべてのint、すべてのdouble、またはすべてのfloatであり、intとdoubleおよびfloatではない)のいずれかを想定できるベクトルです。

従来このようなクラスがない理由は、各アイテムがメモリ内で異なるブロックサイズを使用し(charはバイト、intは4バイトなど)、ルックアップ時に追加のリソースを必要とするためです。何を期待するかを知るために(通常のストレージは連続しています...ベクトルは「基本的に」配列であるとすると)。

あなたがあなた自身を構築しようとしているなら(私は試しました)、あなたはvoid *ポインタ、動的メモリ割り当て、そして型キャストを含む多くの頭痛の種を見ています(私は舞台裏でアイテムを正しく型キャストするための自動化された方法を知りません、しかし他の人はチップインできるかもしれません)。

于 2011-09-18T19:44:32.413 に答える