C++テンプレートに複数のtypename引数を含めるにはどうすればよいですか?
#ifndef _CALL_TEMP_H
#define _CALL_TEMP_H
#include <string>
#include <iostream>
template <typename Sig>
class Foo;
template <typename A, typename B>
class Foo
{
public:
void output() {
std::cout << a_ << b_ << std::endl;
}
A a_;
B b_;
};
template <typename A, typename B, typename C>
class Foo
{
public:
void output() {
std::cout << a_ << b_ << c_ << std::endl;
}
A a_;
B b_;
C c_;
};
#endif
使用法:
int main()
{
Foo<int ,int> doubleint;
doubleint.a_ = 1;
doubleint.b_ = 2;
doubleint.output();
// Foo<int , int , std::string> comp;
// comp.a_ = 1;
// comp.b_ = 2;
// comp.c_ = "haha";
// comp.output();
return 0;
}
しかし、コンパイルされません。どうすればコンパイルできますか?
可変長テンプレートを使用してプライマリテンプレートを宣言し、サポートされているテンプレート引数の数ごとに特化します。例えば:
#ifndef CALL_TEMP_H
#define CALL_TEMP_H
#include <iostream>
template <typename...> class Foo;
template <typename A, typename B>
class Foo<A, B>
{
public:
void output() {
std::cout << a_ << b_ << '\n';
}
A a_;
B b_;
};
template <typename A, typename B, typename C>
class Foo<A, B, C>
{
public:
void output() {
std::cout << a_ << b_ << c_ << '\n';
}
A a_;
B b_;
C c_;
};
#endif
C++ 11を使用できず、テンプレートのデフォルト引数を使用して可変引数リストをシミュレートする必要がある同様の表記法を保持したい場合。これにより、templare引数の数が暗黙的に制限されますが、とにかくテンプレートを専門にしているため、この制限はそれほど重要ではありません。
別の表記法を使用することが許容される場合は、関数宣言のように見えるものを使用して、テンプレートをインスタンス化および特化することもできます。
template <typename> class Foo;
template <typename A, typename B>
class Foo<void(A, B)> {
...
};
template <typename A, typename B, typename C>
class Foo<void(A, B, C)> {
...
};
...
Foo<void(int, int)> f2;
Foo<void(int, int, std::string)> f3;
表記法の変更が許容されるかどうかは、クラステンプレートの使用に依存します。ただし、C++ 11なしの可変長テンプレートのように理想的なソリューションを達成することはできません。
ところで、 std::endl
を使いすぎないでください :'\n'
を使用して行末を意味します。本当にストリームをフラッシュする場合は、std::flush
を使用します。また、_CALL_TEMP_H
は、アンダースコアで始まり大文字が続くすべての名前と同様に、標準C++ライブラリに予約されている名前です。do notuseこれらの名前は、使用する明示的な許可がない限り、独自のコードで使用します(例:__FILE__
および__LINE__
は予約されていますが、使用する明示的な許可が付与されます)。
テンプレートのバージョンが複数ある場合、単一のバージョンを特化する必要があります。別の数の引数が必要な場合は、タグクラスを使用して「この引数は引数ではありません」と言い、それをデフォルト引数として使用します。
あなたの場合、次のようなものが動作します(コンパイルおよびテスト済み):
#include <iostream>
// tag class indicating "no member in this place"
struct nothing {};
template <typename A, typename B, typename C = nothing> // <- note default arg.
class Foo;
template <typename A, typename B>
class Foo<A, B, nothing> // <- note specialization
{
public :
void output() {
std::cout << a_ << b_ << std::endl;
}
A a_;
B b_;
};
template <typename A, typename B, typename C>
class Foo
{
public :
void output() {
std::cout << a_ << b_ << c_ << std::endl;
}
A a_;
B b_;
C c_;
};
int main()
{
Foo<int, int> doubleint;
doubleint.a_ = 1;
doubleint.b_ = 2;
doubleint.output();
Foo<int, int, int> tripleint;
tripleint.a_ = 1;
tripleint.b_ = 2;
tripleint.c_ = 3;
tripleint.output();
}
これは本質的にboost :: Tuple <>/std :: Tuple <>の再発明であることに注意してください。
特殊化と同じクラス名のオーバーロードを混同していると思います。複数のテンプレート引数を持つ同じ名前のクラスを作成することはできません。
テンプレート引数の数が異なる同じクラスを複数回定義することはできないため、コンパイルされません。
サポートするテンプレートパラメータの最大数がわかっている場合は、部分的な特殊化を使用できます。
// main template
template <typename A, typename B = void, typename C = void>
struct Foo
{
void output() { std::cout << a_ << b_ << c_ << std::endl; }
A a_;
B b_;
C c_;
};
// Partial specialisation for two parameters
template <typename A, typename B>
struct Foo<A, B, void>
{
void output() { std::cout << a_ << b_ << c_ << std::endl; }
A a_;
B B_;
};
// Partial specialisation for one parameter
template <typename A>
struct Foo<A, void, void>
{
void output() { std::cout << a_ << std::endl; }
A a_;
};
C++ 11を使用している場合、別のオプションは可変長テンプレートを使用することです。