バリアントクラスの次の単純化されたコードを検討してください。そのほとんどは情報提供のためのものですが、質問はconditional_invoke
メソッドに関するものです。複数のパラメータパックを使用するC++メソッド
// Possible types in variant.
enum class variant_type { empty, int32, string };
// Actual data store.
union variant_data {
std::int32_t val_int32;
std::string val_string;
inline variant_data(void) { /* Leave uninitialised */ }
inline ~variant_data(void) { /* Let variant do clean up. */ }
};
// Type traits which allow inferring which type to use (these are actually generated by a macro).
template<variant_type T> struct variant_type_traits { };
template<class T> struct variant_reverse_traits { };
template<> struct variant_type_traits<variant_type::int32> {
typedef std::int32_t type;
inline static type *get(variant_data& d) { return &d.val_int32; }
};
template<> struct variant_reverse_traits<std::int32_t> {
static const variant_type type = variant_type::int32;
inline static std::int32_t *get(variant_data& d) { return &d.val_int32; }
};
template<> struct variant_type_traits<variant_type::string> {
typedef std::string type;
inline static type *get(variant_data& d) { return &d.val_string; }
};
template<> struct variant_reverse_traits<std::string> {
static const variant_type type = variant_type::string;
inline static std::string *get(variant_data& d) { return &d.val_string; }
};
// The actual variant class.
class variant {
public:
inline variant(void) : type(variant_type::empty) { }
inline ~variant(void) {
this->conditional_invoke<destruct>();
}
template<class T> inline variant(const T value) : type(variant_type::empty) {
this->set<T>(value);
}
template<class T> void set(const T& value) {
this->conditional_invoke<destruct>();
std::cout << "Calling data constructor ..." << std::endl;
::new (variant_reverse_traits<T>::get(this->data)) T(value);
this->type = variant_reverse_traits<T>::type;
}
variant_data data;
variant_type type;
private:
template<variant_type T> struct destruct {
typedef typename variant_type_traits<T>::type type;
static void invoke(type& v) {
std::cout << "Calling data destructor ..." << std::endl;
v.~type();
}
};
template<template<variant_type> class F, class... P>
inline void conditional_invoke(P&&... params) {
this->conditional_invoke0<F, variant_type::int32, variant_type::string, P...>(std::forward<P>(params)...);
}
template<template<variant_type> class F, variant_type T, variant_type... U, class... P>
void conditional_invoke0(P&&... params) {
if (this->type == T) {
F<T>::invoke(*variant_type_traits<T>::get(this->data), std::forward<P>(params)...);
}
this->conditional_invoke0<F, U..., P...>(std::forward<P>(params)...);
}
template<template<variant_type> class F, class... P>
inline void conditional_invoke0(P&&... params) { }
};
コードがこのように動作する、すなわち、それがあれば空であるファンクタのパラメータリストP...
として働きます。私は
template<variant_type T> struct print {
typedef typename variant_type_traits<T>::type type;
static void invoke(type& v, std::ostream& stream) {
stream << v;
}
};
のような別のファンクタを追加し、それが
friend inline std::ostream& operator <<(std::ostream& lhs, variant& rhs) {
rhs.conditional_invoke<print>(lhs);
return lhs;
}
20115 VSコンパイラは
error C2672: 'variant::conditional_invoke0': no matching overloaded function found
やgccのを文句
起動しようとすると、それぞれerror: no matching function for call to 'variant::conditional_invoke0 >&>(std::basic_ostream&)'
私は推測しますコンパイラはデシューできませんU...
が終了し、P...
が開始したとき。問題を回避する方法はありますか?
これは、いくつかの主要な変更を必要とする予定ですが、私は... 'variant_typeを捨て'、代わりにタプルを使用します。このようにして 'template F、variant_type T、typename VAR_TYPES、class ... P>'を持つことになり、後で 'VAR_TYPES'を展開することができます。 –
Frank
'template variant_type_list {};構造体が必要です([** demo **](http://coliru.stacked-crooked.com/a/7083d534b3f19b5e) –
ありがとうございますあなたの提案。私があなたを正しく理解しているとすれば、パックの1つを別の構造にグループ化し、単一の型として渡すことができ、可変引数として 'P ... 'を残します。私がatmを理解していないのは、メソッドのシグネチャが、 'conditional_invoke0'の' VAR_TYPES'/'variant_type_list'から現在の型を取得できるように見えるようにする方法です。 – Christoph