C++ クラス テンプレートを作成しようとしています。私が欲しいのは、このクラステンプレートがユーザー定義クラスで使用される場合です。それらのユーザー定義クラスに、たとえば、to_data
およびなどの特定のメソッドを実装するように強制したいですfrom_data
。基本的な C++ プリミティブ データ型にはそれらは必要ありません。どうすればいいですか?たとえばstd::vector
、クラスのコピー コンストラクターが使用できない場合、コンパイル エラーが発生します。
2 に答える
クラス テンプレートでメソッドを使用するだけです。
template <typename T>
struct Serializer
{
void serialize(T const & t) const { write(t.to_data()); }
void deserialize(T & t) const { t.from_data(read()); }
};
テンプレートをインスタンス化する型に適切なメンバー関数があれば、すべて問題ありません。そうでない場合、コンパイラはエラーをトリガーします。
struct Foo
{
int val;
int to_data() const { return val; }
void from_data(int i) { val = i; }
};
struct Bar {};
Serializer<Foo> sf;
sf.serialize(); // OK
Serializer<Bar> sb;
sb.serialize(); // compiler error: Bar has no member function named "to_data"
コンパイラ エラーは、クラス テンプレートのいくつかの関数を使用しようとしたときにのみ発生することに注意してください。これは、クラス テンプレートのメンバー関数は、使用時にのみインスタンス化 (コンパイル) されるためです。したがって、 andメンバー関数を使用しない限り、Serializer
でインスタンス化してもまったく問題ありません。Bar
serialize
deserialize
2番目の問題、つまりプリミティブ型に異なる動作を提供する方法については、いくつかの解決策があります。1 つ目は、異なる方法で処理したい型に合わせてクラス テンプレートを特殊化することです。たとえば、次のコードSerializer
は処理がint
異なるように特殊化されています。
template <>
struct Serializer<int>
{
void serialize(int i) const { write(i); }
void deserialize(int & i) const { i = read();
};
ただし、これは、実際には同じ方法で処理されるものがある場合でも、特定の型ごとに特殊化を作成することを意味します。
それほど面倒でない解決策は、型の特性を使用しstd::enable_if
、引数の型のいくつかの特性 (この場合はプリミティブかどうか) に応じて正しい実装を選択することです。
#include <type_traits>
template <typename T, typename Enable = void>
struct Serializer
{
// same as before
};
// Partial specialization for fundamental types
template <typename T>
struct Serializer<T, typename
std::enable_if<std::is_fundamental<T>::value>::type>
{
void serialize(T t) const { write(t); }
void deserialize(T & t) const { t = read(); }
};
Serializer<Foo> sf; // first implementation
Serializer<int> si; // second (specialized) implementation
ユーザーの純粋仮想関数によって実装する必要があるメソッドを作成できます。基本的な C++ プリミティブ データ型が必要ない場合は、これらの状況に合わせてテンプレートを特殊化し、これらの場合にデフォルトの実装を提供できます。