2017-01-02 8 views
0

バリアントクラスの次の単純化されたコードを検討してください。そのほとんどは情報提供のためのものですが、質問は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...が開始したとき。問題を回避する方法はありますか?

+0

これは、いくつかの主要な変更を必要とする予定ですが、私は... 'variant_typeを捨て'、代わりにタプルを使用します。このようにして 'template F、variant_type T、typename VAR_TYPES、class ... P>'を持つことになり、後で 'VAR_TYPES'を展開することができます。 – Frank

+2

'template variant_type_list {};構造体が必要です([** demo **](http://coliru.stacked-crooked.com/a/7083d534b3f19b5e) –

+0

ありがとうございますあなたの提案。私があなたを正しく理解しているとすれば、パックの1つを別の構造にグループ化し、単一の型として渡すことができ、可変引数として 'P ... 'を残します。私がatmを理解していないのは、メソッドのシグネチャが、 'conditional_invoke0'の' VAR_TYPES'/'variant_type_list'から現在の型を取得できるように見えるようにする方法です。 – Christoph

答えて

0

両方のパラメータパックを無効にする必要があります。つまり、タイプおよび非タイプのテンプレートパラメータを関数パラメータリストの一部にすることができます。そのために、ダミー構造を導入:

template <variant_type...> 
struct variant_type_list {}; 

をし、コンパイラは、関数呼び出しからvariant_type...パックを推測してみましょう:

template <template <variant_type> class F 
     , variant_type T 
     , variant_type... U 
     , typename... P> 
void conditional_invoke0(variant_type_list<T, U...> t 
         , P&&... params) 
{ 
    if (this->type == T) 
    { 
     F<T>::invoke(*variant_type_traits<T>::get(this->data) 
        , std::forward<P>(params)...); 
    } 

    this->conditional_invoke0<F>(variant_type_list<U...>{} 
           , std::forward<P>(params)...); 
} 

variant_type_listに過負荷を導入、再帰呼び出しを解除するには:

template <template <variant_type> class F, typename... P> 
void conditional_invoke0(variant_type_list<>, P&&... params) {} 

呼び出し元を初めて呼び出すときは、引数としてvariant_typesを指定してください。

this->conditional_invoke0<F>(variant_type_list<variant_type::int32, variant_type::string>{} 
          , std::forward<P>(params)...); 

DEMO

関連する問題