2017-04-06 5 views
1

私のプロジェクトの一つでは、私はboost-variantを過度に使っています。ある時点で、私はboost-variantのテンプレートパラメータの最大数(20)を超えました。そこで、いくつかのタイプをリンクされたリストのようにリンクして、以下の解決法を導き出しました。コード複製を削除するためにboost :: static-visitorから派生しました

#include <boost/variant.hpp> 
#include <iostream> 

template<int T> struct A { 
    const int value = T; 
}; 

typedef boost::variant< 
    A<20>,A<21>,A<22>,A<23>,A<24>,A<25>,A<26>,A<27>,A<28>,A<29>,A<30>,A<31>,A<32>,A<33>,A<34>,A<35>,A<36>,A<37>,A<38>,A<39> 
> NextVar; 

typedef boost::variant< 
    A<1>,A<2>,A<3>,A<4>,A<5>,A<6>,A<7>,A<8>,A<9>,A<10>,A<11>,A<12>,A<13>,A<14>,A<15>,A<16>,A<17>,A<18>,A<19>,NextVar 
> TVar; 

struct PrintVisitor : public boost::static_visitor<std::string> { 
    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(*this); 
    } 

    template<int T> 
    result_type operator()(const A<T>& a) { 
     return std::to_string(a.value); 
    } 
}; 

struct IntVisitor : public boost::static_visitor<int> { 
    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(*this); 
    } 

    template<int T> 
    result_type operator()(const A<T>& a) { 
     return a.value; 
    } 
}; 

template<int I> 
struct AddVisitor : public boost::static_visitor<int> { 
    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(*this); 
    } 

    template<int T> 
    result_type operator()(const A<T>& a) { 
     return a.value+I; 
    } 
}; 

int main(int argc, char **args) { 
    TVar x = A<35>(); 
    PrintVisitor v1; 
    std::cout << x.apply_visitor(v1) << std::endl; 
    IntVisitor v2; 
    std::cout << x.apply_visitor(v2) << std::endl; 
    AddVisitor<10> v3; 
    std::cout << x.apply_visitor(v3) << std::endl; 
} 

この回避策が私の問題をどれほどうまく解決したかは本当に驚きました。まだ塩の穀物があった。訪問者ごとに、次の行を含める必要がありました。

result_type operator()(const NextVar& n) { 
    return n.apply_visitor(*this); 
} 

これは、不要なコードの重複のようです。もっと悪いことに、もし私がboost-variantで60以上のタイプを必要とするならば。私は、以下に示すようにBaseVisitorから派生する問題を解決するだろうと、思っ

template<typename T> 
struct BaseVisitor : public boost::static_visitor<T> { 
    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(*this); 
    } 
}; 

:私の試みは私の訪問者のすべてに共通の基本クラスを定義することでした

struct PrintVisitor : public BaseVisitor<std::string> { 
    template<int T> 
    result_type operator()(const A<T>& a) { 
     return std::to_string(a.value); 
    } 
}; 

しかし、その代わりに、コンパイラは文句:

template-argument for "const A<T> &" could not be derived from "T19" 

この種の問題に最も近い回避策はありますか?

答えて

2

まず、この制限値を0,で固定してください。

コードについて:コンパイルした場合でも、BaseVisitor::operator()は、その時点でBaseVisitorとして考えられる*thisの無限再帰を行います。
それを避けるために、あなたの代わりに良いderived()を持ってCRTPを使用することができます。

template<class Derived, typename T> 
struct BaseVisitor : public boost::static_visitor<T> { 
    using typename boost::static_visitor<T>::result_type; 

    Derived & derived() { return static_cast<Derived &>(*this); } 

    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(derived()); 
    } 
}; 

は、その後必要に応じresult_typeだけでなく、(あなたの派生クラスの(そうでない場合は新しいものでは非表示)の範囲には、この同じoperator()をもたらしますテンプレートクラス用)。エイリアスなし

DEMO

としては、コメントでは、エイリアスは各派生クラスに書き込まれなければならないと述べました。それを取り除くために、我々は、基本クラスで同じレベルで2 operator()秒を収集し、異なっ派生機能(ここではvisit)名前を付けることができます。

struct PrintVisitor : public BaseVisitor<PrintVisitor, std::string> { 
    template<int I> 
    std::string visit(const A<I>& a) { 
     return std::to_string(a.value); 
    } 
}; 
:で私たちを残し

template<class Derived, typename T> 
struct BaseVisitor : public boost::static_visitor<T> { 
    using typename boost::static_visitor<T>::result_type; 

    Derived & derived() { return static_cast<Derived &>(*this); } 

    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(derived()); 
    } 
    template<int I> 
    result_type operator()(const A<I>& a) { 
     return derived().visit(a); 
    } 
}; 

DEMO

+0

速い返信と素晴らしいソリューションをありがとう。残念ながら、いくつかの制限があり、 'BOOST_MP_LIMIT_LIST_SIZE'を使用することはできません。あなたのソリューションは間違いなく機能しています。 CRTPはそれほど明らかではありません。 :-) 派生クラスでクラッタ(テンプレートエイリアスコード)を削除することは可能でしょうか? – Aleph0

+0

@FrankSimonデモの31行目と44行目を除く(テンプレートクラスではありません)、いいえ。しかし、私たちのコードを少し修正することによって...版を参照してください。 –

+0

本当に素晴らしい。あなたのソリューションを消化するまでには時間がかかります。どうもありがとう。私はプロダクションコードでそれを使用します。 – Aleph0

関連する問題